Why is 0 < -0x80000000?
Solution 1:
This is quite subtle.
Every integer literal in your program has a type. Which type it has is regulated by a table in 6.4.4.1:
Suffix Decimal Constant Octal or Hexadecimal Constant
none int int
long int unsigned int
long long int long int
unsigned long int
long long int
unsigned long long int
If a literal number can't fit inside the default int
type, it will attempt the next larger type as indicated in the above table. So for regular decimal integer literals it goes like:
- Try
int
- If it can't fit, try
long
- If it can't fit, try
long long
.
Hex literals behave differently though! If the literal can't fit inside a signed type like int
, it will first try unsigned int
before moving on to trying larger types. See the difference in the above table.
So on a 32 bit system, your literal 0x80000000
is of type unsigned int
.
This means that you can apply the unary -
operator on the literal without invoking implementation-defined behavior, as you otherwise would when overflowing a signed integer. Instead, you will get the value 0x80000000
, a positive value.
bal < INT32_MIN
invokes the usual arithmetic conversions and the result of the expression 0x80000000
is promoted from unsigned int
to long long
. The value 0x80000000
is preserved and 0 is less than 0x80000000, hence the result.
When you replace the literal with 2147483648L
you use decimal notation and therefore the compiler doesn't pick unsigned int
, but rather tries to fit it inside a long
. Also the L suffix says that you want a long
if possible. The L suffix actually has similar rules if you continue to read the mentioned table in 6.4.4.1: if the number doesn't fit inside the requested long
, which it doesn't in the 32 bit case, the compiler will give you a long long
where it will fit just fine.
Solution 2:
0x80000000
is an unsigned
literal with value 2147483648.
Applying the unary minus on this still gives you an unsigned type with a non-zero value. (In fact, for a non-zero value x
, the value you end up with is UINT_MAX - x + 1
.)
Solution 3:
This integer literal 0x80000000
has type unsigned int
.
According to the C Standard (6.4.4.1 Integer constants)
5 The type of an integer constant is the first of the corresponding list in which its value can be represented.
And this integer constant can be represented by the type of unsigned int
.
So this expression
-0x80000000
has the same unsigned int
type. Moreover it has the same value
0x80000000
in the two's complement representation that calculates the following way
-0x80000000 = ~0x80000000 + 1 => 0x7FFFFFFF + 1 => 0x80000000
This has a side effect if to write for example
int x = INT_MIN;
x = abs( x );
The result will be again INT_MIN
.
Thus in in this condition
bal < INT32_MIN
there is compared 0
with unsigned value 0x80000000
converted to type long long int according to the rules of the usual arithmetic conversions.
It is evident that 0 is less than 0x80000000
.
Solution 4:
The numeric constant 0x80000000
is of type unsigned int
. If we take -0x80000000
and do 2s compliment math on it, we get this:
~0x80000000 = 0x7FFFFFFF
0x7FFFFFFF + 1 = 0x80000000
So -0x80000000 == 0x80000000
. And comparing (0 < 0x80000000)
(since 0x80000000
is unsigned) is true.