[Discussion] Dealing with signed unsigned mismatc

Oct 18, 2014 at 2:08am
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.
Oct 18, 2014 at 2:36am
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.
Oct 18, 2014 at 2:58am
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.
Oct 18, 2014 at 4:15am
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.
Oct 18, 2014 at 4:27am
assert(value >= 0) checks if the value is positive, not if it overflows.
Oct 18, 2014 at 5:11am
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.
Oct 18, 2014 at 5:23am
Either way though, using assert wouldn't take care of the warning.
Oct 18, 2014 at 6:01am
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
Oct 18, 2014 at 6:44am
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.
Oct 18, 2014 at 7:11am
Doesn't happen in clang or msvc, be better off disabling that with -fwrapv.

One would be better off not depending on undefined behavior.
Oct 18, 2014 at 2:01pm
It is a shame it is undefined behavior, it really shouldn't be, not just for the sake of some optimizations.
Oct 18, 2014 at 2:34pm
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.
Oct 18, 2014 at 2:57pm
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
    
    // ... 

}
Oct 18, 2014 at 3:02pm
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.