Why does the smallest int, −2147483648, have type 'long'? [duplicate] Why does the smallest int, −2147483648, have type 'long'? [duplicate] c c

Why does the smallest int, −2147483648, have type 'long'? [duplicate]


In C, -2147483648 is not an integer constant. 2147483648 is an integer constant, and - is just a unary operator applied to it, yielding a constant expression. The value of 2147483648 does not fit in an int (it's one too large, 2147483647 is typically the largest integer) and thus the integer constant has type long, which causes the problem you observe. If you want to mention the lower limit for an int, either use the macro INT_MIN from <limits.h> (the portable approach) or carefully avoid mentioning 2147483648:

printf("PRINTF(d) \t: %d\n", -1 - 2147483647);


The problem is that -2147483648 is not an integer literal. It's an expression consisting of the unary negation operator - and the integer 2147483648, which is too big to be an int if ints are 32 bits. Since the compiler will choose an appropriately-sized signed integer to represent 2147483648 before applying the negation operator, the type of the result will be larger than an int.

If you know that your ints are 32 bits, and want to avoid the warning without mutilating readability, use an explicit cast:

printf("PRINTF(d) \t: %d\n", (int)(-2147483648));

That's defined behaviour on a 2's complement machine with 32-bit ints.

For increased theoretical portability, use INT_MIN instead of the number, and let us know where you found a non-2's-complement machine to test it on.


To be clear, that last paragraph was partly a joke. INT_MIN is definitely the way to go if you mean "the smallest int", because int varies in size. There are still lots of 16-bit implementations, for example. Writing out -231 is only useful if you definitely always mean precisely that value, in which case you would probably use a fixed-sized type like int32_t instead of int.

You might want some alternative to writing out the number in decimal to make it clearer for those who might not notice the difference between 2147483648 and 2174483648, but you need to be careful.

As mentioned above, on a 32-bit 2's-complement machine, (int)(-2147483648) will not overflow and is therefore well-defined, because -2147483648 will be treate as a wider signed type. However, the same is not true for (int)(-0x80000000). 0x80000000 will be treated as an unsigned int (since it fits into the unsigned representation); -0x80000000 is well-defined (but the - has no effect if int is 32 bits), and the conversion of the resulting unsigned int 0x80000000 to int involves an overflow. To avoid the overflow, you would need to cast the hex constant to a signed type: (int)(-(long long)(0x80000000)).

Similarly, you need to take care if you want to use the left shift operator. 1<<31 is undefined behaviour on 32-bit machines with 32-bit (or smaller) ints; it will only evaluate to 231 if int is at least 33 bits, because left shift by k bits is only well-defined if k is strictly less than the number of non-sign bits of the integer type of the left-hand argument.

1LL<<31 is safe, since long long int is required to be able to represent 263-1, so its bit size must be greater than 32. So the form

(int)(-(1LL<<31))

is possibly the most readable. YMMV.


For any passing pedants, this question is tagged C, and the latest C draft (n1570.pdf) says, with respect to E1 << E2, where E1 has a signed type, that the value is defined only if E1 is nonnegative and E1 × 2E2 "is representable in the result type". (§6.5.7 para 4).

That's different from C++, in which the application of the left-shift operator is defined if E1 is nonnegative and E1 × 2E2 "is representablein the corresponding unsigned type of the result type" (§5.8 para. 2, emphasis added).

In C++, according to the most recent draft standard, the conversion of an integer value to a signed integer type is implementation-defined if the value cannot be represented in the destination type (§4.7 para. 3). The corresponding paragraph of the C standard -- §6.3.1.3 para. 3 -- says that "either the result is implementation-defined or an implementation-defined signal is raised".)