2
$\begingroup$

In Computer Systems: a Programmer's Perspective:

Writing TMin in C In Figure 2.19 and in Problem 2.21, we carefully wrote the value of TMin32 as -2,147,483,647-1. Why not simply write it as either -2,147,483,648 or 0x80000000? Looking at the C header file limits.h, we see that they use a similar method as we have to write TMin32 and TMax32:

/* Minimum and maximum values a ‘signed int’ can hold. */
#define INT_MAX 2147483647
#define INT_MIN (-INT_MAX - 1)

Unfortunately, a curious interaction between the asymmetry of the two’s-complement representation and the conversion rules of C forces us to write TMin32 in this unusual way. Although understanding this issue requires us to delve into one of the murkier corners of the C language standards, it will help us appreciate some of the subtleties of integer data types and representations.

0x80000000 is a hexadecimal notation, and is in the range of signed int, isn't it? (How do you tell if an integer integral is signed or unsigned? Isn't it that an integer literal without any suffix by default is a signed integer? So 0X80000000 is signed? It is in the range of signed, because it is the smallest integer in the signed range)

Should #define INT_MIN 0x80000000 be okay, while the book says otherwise?

$\endgroup$
  • $\begingroup$ Think also about readability, code is meant to be readable by humans. It's easier (at least for me) if I look up the definition and I see -2147483648 (or some value, negated, +/- 1) instead of having to figure out. I know it's not a lot of work to figure out 0x80000000 or -020000000000, but still. $\endgroup$ – Gizmo Oct 12 at 7:20
5
$\begingroup$

No it's not OK.

There are no negative literals per-se in C. "-12345" is interpreted as the combination of the positive literal 12345 with the unary minus operator.

0x80000000 is outside the range of int and will be interpreted as a literal of type unsigned int.

On most compilers you could get around this by using a typecast

#define INT_MIN ((int)(0x80000000))

However the behavior of this expression is implementation defined. Most compilers will just reinterpret the bits, but there may be some that do not.

So 0X80000000 is signed? It is in the range of signed, because it is the smallest integer in the signed range

No,

If we take the smallest signed int on a twos-complement system with 32-bit int and reinterpret those bits as an unsigned int we get the value 0X80000000.

But 0x80000000 itself is still a positive number, it is not the same as INT_MIN (though due to Cs type conversion rules it may sometimes compare equal to it).

How do you tell if an integer integral is signed or unsigned?

C chooses the type of an integer literal by going through a list of types and choosing the first one that can correctly represent the literal.

Exactly what is in said list of types depends on the version of the C standard and on the radix of the literal. In modern versions of C for decimal literals the compiler strongly prefers signed types, only using an unsigned type if none of the standard signed types (int, long or long long*) can fit the literal. For hexadecimal literals the preference for signed types is weaker, signed int is still preferred over unsigned int, but unsigned int is preferred over long and long long.

So when the compiler looks at 0x80000000 on a system with 32-bit ints it concludes it will not fit in an int, but it will fit in an unsigned int.

* Note: in C89 long long wasn't a required type, so a C89 compiler may use unsigned long instead of signed long long for a literal.

| cite | improve this answer | |
$\endgroup$
9
$\begingroup$

This is more of a question appropriate to the CS forums, but here is the short of it: it requires you to know how the compiler parses numbers. In particular, when the compiler sees something like -2147483648, it parses this as unary minus applied to 2147483648. But the latter (a positive number) is too large for the int data type, and so may be wrapped around or otherwise modified. The same is the case for 0x80000000: It is parsed as an integer, but is too large for the int data type.

On the other hand, #define INT_MIN (-INT_MAX - 1) works: every constant in that expression fits into the int data type.

| cite | improve this answer | |
$\endgroup$
  • $\begingroup$ How do you tell if an integer integral is signed or unsigned? Isn't it that an integer literal without any suffix by default is a signed integer? So 0X80000000 is signed? It is in the range of signed, because it is the smallest integer in the signed range. $\endgroup$ – Tim Oct 10 at 21:35
  • 2
    $\begingroup$ @Tim: All numeric literals are non-negative in C. The unary - operator is separate from literal parsing, that's exactly why -2147483648 wouldn't be a valid definition on a machine with 32-bit 2's complement int: since 2147483648 doesn't fit in an int, it has type unsigned int or long (or long long). (And unary - doesn't shrink its operand back to a smaller type, even for compile-time constants). But we want INT_MIN to have type signed int, not long. 0x80000000 has exactly the same problem as 2147483648 - those are the same number. $\endgroup$ – Peter Cordes Oct 11 at 5:47
  • $\begingroup$ Note that this is is a C rule. Another language could define negative literals, and then the problems goes away. C is positively ancient. $\endgroup$ – MSalters Oct 12 at 7:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.