Strange behavior in C when comparing integers
This issue occurs in my mac environment where the gcc version is shown below:
Configured with: --prefix=/Library/Developer/CommandLineTools/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Apple LLVM version 7.0.2 (clang-700.1.81)
Target: x86_64-apple-darwin14.5.0
Thread model: posix
The code segment refers to one of the functions.
int someFunction(int x, int n) {
int TMin_n = -(1 << (n-1));
int TMax_n = (1 << (n-1)) - 1;
int a = -2147483648;
printf("%s\n", x == a ? "true" : "false"); // true
printf("%s\n", x <= TMax_n ? "true" : "false"); // false
printf("%s\n", a <= TMax_n ? "true" : "false"); // true
printf("%d %d %d\n", TMin_n, x, TMax_n); // -2147483648 -2147483648 2147483647
return 0;
}
a equals x, but why do they have different behavior when compared to TMax_n?
UPDATE
all data types in this code segment int
.
UPDATE2
Thanks everyone. I just tried the same code on an ubuntu machine and it works fine. It only got stuck on my MAC.Seems that it is a compiler related issue.
source to share
It seems that due to undefined behavior in the expression (1 << (n-1)) - 1
, especially in (1 << (n-1))
since it is 1
treated as int
(possibly 32 bits) and the expression (1 << (n-1))
then gives a value that exceeds the maximum value represented by the character int
. So this (intermediate) result results in an integer arithmetic overflow value, which is UB (see cppreference ):
When an integer arithmetic overflow operation is declared (the result does not match the type of the result), the behavior is undefined: it can be wrapped around according to presentation rules (usually 2's complement), it can depend on some platforms, or because of compiler capabilities (e.g. -ftrapv in GCC and Clang), or the compiler can be fully optimized.
Therefore, it may work on some compilers, it may not work on others. However, if you added to unsigned int
before the bit-crash, the overflow will disappear and you will fall back into certain (and expected) behavior:
(int) (((unsigned)1 << (n-1)) - 1)
BTW: if you set -ftrapv
to "other compiler options" for the C compiler, expressions int TMin_n = -(1 << (n-1))
and will int TMax_n = (1 << (n-1)) - 1
throw a runtime exception (for example EXC_BAD_INSTRUCTION
).
source to share
I tried your snippet using the clang online compiler and got the results from the question. I am assuming that the code (including the function call) is fully inlined and then the compiler will optimize any comparisons it finds constant.
This line seems to be:
int TMax_n = (1 << (n-1)) - 1;
If I explicitly cast (1 << (n-1))
in unsigned
, the problem goes away:
int TMax_n = (int) ((unsigned)(1 << (n-1)) - 1);
source to share