I was working on an embedded project when I ran into something which I thought was strange behaviour. I managed to reproduce it on codepad (see below) to confirm, but don't have any other C compilers on my machine to try it on them.
Scenario: I have a #define
for the most negative value a 32-bit integer can hold, and then I try to use this to compare with a floating point value as shown below:
#define INT32_MIN (-2147483648L)
void main()
{
float myNumber = 0.0f;
if(myNumber > INT32_MIN)
{
printf("Everything is OK");
}
else
{
printf("The universe is broken!!");
}
}
Codepad link: http://codepad.org/cBneMZL5
To me it looks as though this this code should work fine, but to my surprise it prints out The universe is broken!!
.
This code implicitly casts the INT32_MIN
to a float
, but it turns out that this results in a floating point value of 2147483648.0
(positive!), even though the floating point type is perfectly capable of representing -2147483648.0
.
Does anyone have any insights into the cause of this behaviour?
CODE SOLUTION: As Steve Jessop mentioned in his answer, limits.h
and stdint.h
contain correct (working) int
range define
s already, so I'm now using these instead of my own #define
PROBLEM/SOLUTION EXPLANATION SUMMARY: Given the answers and discussions, I think this is a good summary of what's going on (note: still read the answers/comments because they provide a more detailed explanation):
- I'm using a C89 compiler with 32-bit
long
s, so any values greater thanLONG_MAX
and less or equal toULONG_MAX
followed by theL
postfix have a type ofunsigned long
(-2147483648L)
is actually a unary-
on anunsigned long
(see previous point) value:-(2147483648L)
. This negation operation 'wraps' the value around to be theunsigned long
value of2147483648
(because 32-bitunsigned long
s have the range0
-4294967295
).- This
unsigned long
number looks like the expected negativeint
value when it gets printed as anint
or passed to a function because it is first getting cast to anint
, which is wrapping this out-of-range2147483648
around to-2147483648
(because 32-bitint
s have the range -2147483648 to 2147483647) - The cast to
float
, however, is using the actualunsigned long
value2147483648
for conversion, resulting in the floating-point value of2147483648.0
.
gcc -std=c90
produces the observed behaviour,gcc -std=c99
works "as expected". – MahoneL
suffix to your constant specifically to avoid the problem described in the answers. However, on your platformint
andlong
have the same size, which is why the overflow persists. You can addLL
suffix instead and the overflow should go away (assuminglong long
has larger range) – Menarcheint main(void)
, notvoid main()
. – PinxitL
suffix areunsigned long
, it's specifically values greater thanLONG_MAX
and less or equal toULONG_MAX
. – Yarndyed