[Discussion] Dealing with signed unsigned mismatc

There are many questions on the web on how to fix a specific signed/unsigned mismatch, and the solution is usually just making one variable unsigned. However, I've never found a decent discussion on dealing with it when you "need" one to be unsigned, and the other to be signed.

I've hit this issue multiple times but I'll just share my most recent.

I have a grid (x,y) which should be unsigned, since you can't have a (-5,-3) sized grid. However, I have a Direction object which should be signed, since I can have a (-1, -1) direction. The problem is when I do something like Location_x + Direction_x > grid_x which throws the signed/unsigned mismatch warning.

Now, there are multiple ways to deal with that one example, but that's not the question.

Rather, what kinds of similar situations have you been in, and how do you prefer to go about dealing with it?

And as a reminder, this is a discussion thread, I'm not looking for an answer to my example.
I try to always use signed types (specifically, int) wherever practical.

This provides the benefit of having uniformity and ease of use among types/functions. I don't have to worry about whether or not this particular function of this particular class is of type unsigned or signed long or unsigned long long or std::size_t because everything is just an int.

Sometimes (like when dealing with file offsets) I will want a bigger type.. so I typically will use int64_t there. But in general... I just stick with ints.


In cases where signed types don't make logical sense (like for array/grid sizes) -- I use them anyway and validate their contents before applying them.
I prefer to avoid signed types because signed overflow is deadly UB, while unsigned overflow is just a wrap-around I can keep under control with asserts and tests.

As for location and direction, it's the same way in the library (size_type vs. difference_type) and even in the core language (size_t vs ptrdiff_t). Nothing to be surprised about.
I prefer to avoid signed types because signed overflow is deadly UB, while unsigned overflow is just a wrap-around I can keep under control with asserts and tests.

It's easy to check for a signed over flow assert(value >= 0), can't say the same for unsigned. If your code doesn't handle negative numbers, and you write it assuming that the numbers will never be negative using signed types then yah it's going to be a problem.
assert(value >= 0) checks if the value is positive, not if it overflows.
Which is what happens if it overflows, just about every CPU uses 2's complement as the integer representation. I don't think i've even seen a CPU that doesn't.
Either way though, using assert wouldn't take care of the warning.
It's easy to check for a signed over flow assert(value >= 0)

Here's an example where this doesn't work:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
#include <cassert>

int main() {
    int i = 1;
    int c = 0;

    do {
        c ++;
        i += i;

        assert(i >= 0);

        std::cout << i << std::endl;
    } while (i >= 0);

    std::cout << c << std::endl;

    return 0;
}

Compiling with speed optimizations enabled yields an infinite
loop: http://coliru.stacked-crooked.com/a/020b338c98730c9e

Explanation: http://stackoverflow.com/a/9025098/2327880
Doesn't happen in clang or msvc, be better off disabling that with -fwrapv. Though clang does do some weird stuff with that code for some reason.
Doesn't happen in clang or msvc, be better off disabling that with -fwrapv.

One would be better off not depending on undefined behavior.
It is a shame it is undefined behavior, it really shouldn't be, not just for the sake of some optimizations.
assert should never be used for logic control. It is called DEBUG assertion for a reason. Its only use is to force program to file and get you a nice debugging point.

1) Assert just shuts you program. You cannot intercept failed assertion, gracefully handle it... It crashes. That's all
2) Asserts are thrown out completely when you compile your program in release mode.
3) assert is a macro.
Assertion of invariants is a staple for trapping logic errors during debugging and testing.

This is canonical.
1
2
3
4
5
6
7
8
9
10
inline void foo( chess_board board, int square ) // invariant: square is a non-negative integer less than 64
{
    assert( square >= 0 && square < 64 ) ; // debug 

    if( square < 0 || square > 63 )
        throw std::out_of_range( "invalid square " + std::to_string(square) ) ; // NDEBUG
    
    // ... 

}
Yes, as debugging aid. Not as only check. My statement Its only use is to force program to fail and get you a nice debugging point. does not contaradict you point.
Topic archived. No new replies allowed.