if(x + 1 < x)
...
is an overflow check, but silently "optimising away" that code because of the assumption that signed integers will never overflow is just horribly hostile and absolutely idiotic behaviour in my opinion. A sensible and pragmatic way to fix this would be to update the standard to define signed overflow, and maybe add a macro that is defined only on non-2s-complement platforms.C99 defines int8_t, if it exists, to be a 2's complement signed integer of exactly 8 bits. Same for 16, 32, etc. The standard could very well define behavior on overflow for these (that is, turn them into actual types instead of typedefs), and leave int, long, etc alone. I think this would be a viable, realistic, solution. Integer conversions would probably still be a pain, though.
Signed integers have some weirdness attached. The number that's one followed by all zeroes in binary (INT_MIN in limits.h) is defined as negative, because the sign bit is set. But, the rules for 2's complement arithmetic predict that -INT_MIN == INT_MIN. So it's not a normal number.
int abs_s(int x) {
assert(x != INT_MIN);
return (x >= 0) ? x : -x;
}(On that note: Are there even machines left to write code for that don't use two's complement? Or 8 bits per byte?)
#include <limits.h>
#include <stdio.h>
void main(void) {
int x = INT_MIN;
printf("INT_MIN = %d\n", x);
printf("INT_MIN * -1 = %d\n", x * -1);
printf("INT_MIN / -1 = %d\n", x / -1);
} $ gcc -std=c99 -S intmin.c -o intmin.s
$ clang -std=c99 -S intmin.c -o intmin.sc
$ grep -i div intmin.s*
intmin.sc: idivl %esi
(No idivl in the gcc version) $ clang --version
Debian clang version 3.5-1~exp1 (trunk) (based on LLVM 3.5)
Target: x86_64-pc-linux-gnu
Thread model: posix
$ clang -o intmin_c -std=c99 intmin.c
$ ./intmin_c
INT_MIN = -2147483648
INT_MIN * -1 = -2147483648
Floating point exception
$ gcc --version
gcc-4.7.real (Debian 4.7.2-5) 4.7.2
(...)
$ gcc -o intmin -std=c99 intmin.c
$ ./intmin
INT_MIN = -2147483648
INT_MIN * -1 = -2147483648
INT_MIN / -1 = -2147483648
$ tcc -v
tcc version 0.9.25
$ tcc -run intmin.c
INT_MIN = -2147483648
INT_MIN * -1 = -2147483648
Floating point exception
$ cat intmin.c
#include <limits.h>
#include <stdio.h>
int main(void) { // Change from void to int for c99
int x = INT_MIN;
printf(" INT_MIN = %d\n", x);
printf("INT_MIN * -1 = %d\n", x * -1);
printf("INT_MIN / -1 = %d\n", x / -1);
return 0; // return 0 - c99
}
So, is this a gcc thing?It's also a good example of how undefined behavior allows for optimizations. The compiler is able to evaluate your expression at compile time rather than emitting a division instruction, even though this changes how the program behaves.
C sure is fun.