Re: Style question: Use always signed integers or not?
Juha Nieminen <nospam@thanks.invalid> wrote:
I was once taught that if some integral value can never have negative
values, it's a good style to use an 'unsigned' type for that: It's
informative, self-documenting, and you are not wasting half of the value
range for values which you will never be using.
I agreed with this, and started to always use 'unsigned' whenever
negative values wouldn't make any sense. I did this for years.
However, I slowly changed my mind: Doing this often causes more
problems than it's worth. A good example:
If you have an image class, its width and height properties can
*never* get negative values. They will always be positive. So it makes
sense to use 'unsigned' for the width and height, doesn't it? What
problems could that ever create?
Well, assume that you are drawing images on screen, by pixel
coordinates, and these images can be partially (or completely) outside
the screen. For example, the left edge coordinate of the image to be
drawn might have a negative x value (for example drawing a 100x100 image
at coordinates <-20, 50>). Since the coordinates are signed and the
dimensions of the image are unsigned, this may cause signed-unsigned
mixup. For example this:
if(x - width/2 < 1) ...
where 'x' is a signed integer, gives *different* results depending on
whether 'width' is signed or unsigned, with certain values of those
variables (for example x=2 and width=10). The intention is to treat
'width' here as a signed value, but if it isn't, the comparison will
malfunction (without explicitly casting 'width' to a signed value). This
may well go completely unnoticed because compilers might not even give
any warning (for example gcc doesn't).
Thus at some point I started to *always* use signed integers unless
there was a very good reason not to. (Of course this sometimes causes
small annoyances because STL containers return an unsigned value for
their size() functions, but that's usually not a big problem.)
It would be interesting to hear other opinions on this subject.
From Stroustrup:
The unsigned integer types are ideal for uses that treat storage as a
bit array. Using an unsigned instead of an int to gain one more bit
to represent positive integers is almost never a good idea. Attempts
to ensure that some values are positive by declaring variables
unsigned will typically be defeated by the implicit conversion rules.
I especially like this quote from Gavin Deane:
Both signed and unsigned arithmetic in C++ behave just like real
world integer arithmetic until you reach the minimum and maximum
values for those types. The difference is that for signed, the
correlation between the real world and C++ breaks down at + or - a
very big number, whereas for unsigned, the correlation between the
real world and C++ breaks down at zero and a (different) very big
number.
In other words, if I use unsigned, I am very close to the edge of the
domain in which C++ arithmetic corresponds to the real world. If I
use signed, I am comfortably in the middle of that domain and the
extremities where I have to start coding for special cases are far,
far away from any number that I am likely to ever want to use for an
age or a length or a size, or I am likely to ever end up with by
doing arithmetic with those quantities.