views:

171

answers:

5

Given the following piece of (pseudo-C++) code:

float x=100, a=0.1;
unsigned int height = 63, width = 63;
unsigned int hw=31;
for (int row=0; row < height; ++row)
{
    for (int col=0; col < width; ++col)
    {
        float foo = x + col - hw + a * (col - hw);
        cout << foo << " ";
    }
    cout << endl;
}

The values of foo are screwed up for half of the array, in places where (col - hw) is negative. I figured because col is int and comes first, that this part of the expression is converted to int and becomes negative. Unfortunately, apparently it doesn't, I get an overflow of an unsigned value and I've no idea why.

How should I resolve this problem? Use casts for the whole or part of the expression? What type of casts (C-style or static_cast<...>)? Is there any overhead to using casts (I need this to work fast!)?

EDIT: I changed all my unsigned ints to regular ones, but I'm still wondering why I got that overflow in this situation.

+2  A: 

How about making height, width, and hw signed ints? What are you really gaining by making them unsigned? Mixing signed and unsigned integers is always asking for trouble. At first glance, at least, it doesn't look like you gain anything by using unsigned values here. So, you might as well make them all signed and save yourself the trouble.

Jonathan M Davis
Can a height or a width be (*semantically*) negative? This leads to other interesting questions, such as negative area and volume. Can a person earn tax money if the area of their house is negative? Will my car fit into a small parking space if it has a negative length? Hmmmm.....
Thomas Matthews
Semantics aren't always the issue. There are plenty of times where signed works just fine, and you're better able to find bugs since you'll end up with negative numbers instead of really small or really big positive ones when wrapping around due to an error. In some situations, however, it does make good sense to use unsigned values rather than signed ones. It depends on what you're doing. The important thing, however, is that you not mix signed and unsigned values. *That* is where the trouble really lies. Still, I'd argue not to use unsigned unless you need it since it's more error prone.
Jonathan M Davis
A: 

If you want this to be fast you should static_cast all unsigned values before you start looping and use their int versions rather than unsigned int. You can still require inputs being unsigned and then just cast them on the way to your algorithm to retain the required domain for your function.

Noah Roberts
+7  A: 

Unsigned integers implement unsigned arithmetic. Unsigned arithmetic is modulo arithmetics. All values are adjusted modulo 2^N, where N is the number of bits in the value representation of unsigned type.

In simple words, unsigned arithmetic always produces non-negative values. Every time the expression should result in negative value, the value actually "wraps around" 2^N and becomes positive.

When you mix a signed and an unsigned integer in a [sub-]expression, the unsigned arithmetic "wins", i.e. the calculations are performed in unsigned domain. For example, when you do col - hw, it is interpreted as (unsigned) col - hw. This means that for col == 0 and hs == 31 you will not get -31 as the result. Instead you wil get UINT_MAX - 31 + 1, which is normally a huge positive value.

Having said that, I have to note that in my opinion it is always a good idea to use unsigned types to represent inherently non-negative values. In fact, in practice most (or at least half) of integer variables in C/C++ the program should have unsigned types. Your attempt to use unsigned types in your example is well justified (if understand the intent correctly). Moreover, I'd use unsigned for col and row as well. However, you have to keep in mind the way unsigned arithmetic works (as described above) and write your expressions accordingly. Most of the time, an expression can be rewritten so that it doesn't cross the bounds of unsigned range, i.e. most of the time there's no need to explicitly cast anything to signed type. Otherwise, if you do need to work with negative values eventually, a well-placed cast to signed type should solve the problem.

AndreyT
+1, you do know what are you talking about.
Matias
I must disagree with the "always a good idea to use unsigned types". My bank account *should* always be positive yes. But using an unsigned quantity means that should something go wrong, I'd end up with a massive positive balance. Ok. perhaps the bank *should* use unsigned quantities on their non credit accounts :P
Chris Becke
The important thing is whether the entity can semantically go negative. The balance in my bank can go negative, but the number of rows in an spreadsheet can't. Only use unsigned if it matches the semantics of your type, and only if you're willing to put in the extra effort to avoid underrun.
Bill
There seem to be two opposing views: some people advise for, others against using unsigned types, both with good reasons. At the moment I veer towards the latter position; unlike strict type control, const-correctness and other mechanisms that help avoid mistakes by enforcing some rules, there's nothing in unsigned datatypes to prevent assignment (or a result) of a negative value, which can (silently) lead to a disaster, IMHO.
neuviemeporte
A: 

You have the conversion rules backwards — when you mix signed and unsigned versions of the same type, the signed operand is converted to unsigned.

Chuck
A: 

Casts won't happen automatically- uncasted arithmetic still has it's uses. The usual example is int / int = int, even if data is lost by not converting to float. I'd use signed int unless it's impossible to do so because of INT_MAX being too small.

DeadMG