Casting minimum 32-bit integer (-2147483648) to float gives positive number (2147483648.0)
c, floating-point, integer, long-integer, minimum
Solution
In C89 with a 32 bit `long`, `2147483648L` has type `unsigned long int` (see 3.1.3.2 Integer constants). So once modulo arithmetic has been applied to the unary minus operation, `INT32_MIN` is the positive value 2147483648 with type `unsigned long`.
In C99, `2147483648L` has type `long` if `long` is bigger than 32 bits, or `long long` otherwise (see 6.4.4.1 Integer constants). So there is no problem and `INT32_MIN` is the negative value -2147483648 with type `long` or `long long`.
Similarly in C89 with `long` larger than 32 bits, `2147483648L` has type `long` and `INT32_MIN` is negative.
I guess you're using a C89 compiler with a 32 bit `long`.
One way to look at it is that C99 fixes a "mistake" in C89. In C99 a decimal literal with no `U` suffix always has signed type, whereas in C89 it may be signed or unsigned depending on its value.
What you should probably do, btw, is include `limits.h` and use `INT_MIN` for the minimum value of an `int`, and `LONG_MIN` for the minimum value of a `long`. They have the correct value and the expected type (`INT_MIN` is an `int`, `LONG_MIN` is a `long`). If you need an exact 32 bit type then (assuming your implementation is 2's complement):
- for code that doesn't have to be portable, you could use whichever type you prefer that's the correct size, and assert it to be on the safe side.
- for code that has to be portable, search for a version of the C99 header `stdint.h` that works on your C89 compiler, and use `int32_t` and `INT32_MIN` from that.
- if all else fails, write `stdint.h` yourself, and use the expression in WiSaGaN's answer. It has type `int` if `int` is at least 32 bits, otherwise `long`.
Problem
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 than `LONG_MAX` and less or equal to `ULONG_MAX` followed by the `L` postfix have a type of `unsigned long` - `(-2147483648L)` is actually a unary `-` on an `unsigned long` (see previous point) value: `-(2147483648L)`. This negation operation 'wraps' the value around to be the `unsigned long` value of `2147483648` (because 32-bit `unsigned long`s have the range `0` - `4294967295`). - This `unsigned long` number looks like the expected negative `int` value when it gets printed as an `int` or passed to a function because it is first getting cast to an `int`, which is wrapping this out-of-range `2147483648` around to `-2147483648` (because 32-bit `int`s have the range -2147483648 to 2147483647) - The cast to `float`, however, is using the actual `unsigned long` value `2147483648` for conversion, resulting in the floating-point value of `2147483648.0`.