3 ms·
The reason I've always preferred unsigned is that it's easier to write code that is correct for the full input domain. Consider for example: extern void g(i
by munch117 5y ago
The reason I've always preferred unsigned is that it's easier to write code that is correct for the full input domain.
Consider for example:
extern void g(int);
void f(int x, int y)
{
g(x - y);
}
The function f obviously doesn't work for all values of x and y: Some values will overflow, some values will underflow.
How do you fix that? Writing an explicit check for whether x-y overflows is highly impractical, no one does that.
Or you could document the limitation using pre- and postconditions that narrow down the range of allowed values to something you know will work:
extern void g(int);
void f(int x, int y)
/*
PRECONDITION: -1000000 <= x <= 1000000 and -1000000 <= y <= 1000000
*/
{
g(x - y);
}
No one does that either. I don't know why. There's no principled reason why you couldn't meticulously keep track of signed int input and output ranges. But that's how it is, no one does it, valid int ranges are always unstated.
Now for the unsigned version of the same problem:
extern void g1(unsigned);
extern void g2(unsigned);
void f(unsigned x, unsigned y)
{
if(x >= y)
g1(x - y);
else
g2(y - x);
}
Assuming g1 and g2 are well-defined for all unsigned inputs, so is f. That was easy.
It may look like more code, because there are two g functions now. But chances are you probably need to treat the x<y case differently somewhere anyway; in the larger scheme of things, unsigned is not more code.
- clappski 5y agoWhat you should be doing for checked arithmetic with GCC is use the builtins for those that aren’t aware; https://gcc.gnu.org/onlinedocs/gcc/Integer-Overflow-Builtins.html https://gcc.gnu.org/onlinedocs/gcc/Integer-Overflow-Builtins...
- munch117 5y agoIt would be nice to see something like that standardised so you could use it in portable code. It doesn't solve the problem though. You still need to write some code for what to do if there's an overflow, and you need separate handling for underflow. So the signed case is now: extern void g1(int); extern void g2(int); extern void g3(int); void f(int x, int y) { int x_minus_y; if(__builtin_ssubl_overflow(x, y, &x_minus_y)) g1(x_minus_y); else if(x > y) g2(...); /* overflow */ else g3(...); /* underflow */ } Work in progress. I've given up on figuring out what arguments to pass to g2 and g3. Since the difference won't fit in an int, you would need to offset-adjust the value somehow in order to fit it in an int. Seems messy. Maybe you can think of something simpler.