5

Why is 1 not greater than -0x80000000. I know it has something to do with overflow. But can someone explain why? is 0x80000000 not a constant I think it is?

assert(1 > -0x80000000);

The assert triggers in C++. Why is that?


I am grateful for some of the answer provided. But does C++ standard define that the constant needs to be stored in a 32 bit integer? Why doesn't compiler recognized that 80000000 isn't going to be fit for a 32 bit integer and use 64 bit for it? I mean, the largest 32 bit int can be 0x7FFFFFFF. 0x80000000 is obviously larger than that. Why does compiler still use 32 bit for that?

Negative Zero
  • 1,224
  • 3
  • 10
  • 19

2 Answers2

13

According to the C and C++ standards, -0x80000000 is not an integer constant. It's an expression, like 3 + 5. In this case, it's the constant 0x80000000, operated upon by the negation operator. For compilers which have 32-bit ints, 0x80000000 is not representable as an int, but is representable as an unsigned int. But negating an unsigned integer is (weirdly) done in an unsigned context. So the negation here effectively has no effect.

Sneftel
  • 40,271
  • 12
  • 71
  • 104
  • Why is it converted to an `unsigned int`? – vsoftco Nov 21 '14 at 01:25
  • Then I don't get it. On my machine, `int` has 64 bits, so enough space to represent `-0x80000000`. How is this expression interpreted? I.e., what type is it promoted to? If I cast it to `(int)` then it's fine, it displays as `-2147483648`, the value it represents. – vsoftco Nov 21 '14 at 01:26
  • 1
    @vsoftco Even if your processor happens to be 64-bit, it's extremely unlikely that your compiler has 64-bit `int`s. – Sneftel Nov 21 '14 at 01:28
  • 1
    @vsoftco It's treated as (NOT converted to) an `unsigned int` because it's not representable as an `int`. – Sneftel Nov 21 '14 at 01:29
  • That's fishy... I used a `cout << typeid(-0x80000000).name();`, and the result is `j`, i.e. `unsigned int`. – vsoftco Nov 21 '14 at 01:29
  • @vsoftco: It doesn't work that way. Some programs depend on the fact that an `int` is 32 bits. A compiler will always aim to produce code that will do exactly the same, regardless of the machine on which it runs. If you for instance would use bitwise operations, this definitely matters. A 64-bit machine simply means it can handle adding two 64 bit numbers in one instruction. – Willem Van Onsem Nov 21 '14 at 01:30
  • Ok, fair enough, just saw now that `sizeof(int)==4`. It's clear now, my (stupid) mistake. I was biased because was using `std::size_t` a lot recently, which IS 64 bits on my system. +1 for answer! – vsoftco Nov 21 '14 at 01:32
  • "*Interestingly, the sign IS part of floating point constants.*" -- Incorrect, unless you're referring to the (optional) sign on the (optional) exponent. `-1.2e-3` is two tokens, a unary `-` operator applied to the floating-point constant `1.2e-3`. – Keith Thompson Nov 21 '14 at 01:48
  • @KeithThompson No, you're right. I misread the standard. – Sneftel Nov 21 '14 at 01:50
  • 2
    @CommuSoft: There is no fact that `int` is 32 bits, and programs that depend on that assumption are non-portable. It is your job, not the compiler's, to make your program run the same on the machines you care about. – Ben Voigt Nov 21 '14 at 01:58
  • @Sneftel "0x80000000 is not representable as an int". Can you explain further as to what does "not representable as an int" means here? Can an int store a hexadecimal value of 0x80000000? I think yes. Why then do we say that 0x80000000 is not representable as an int? Any references to support your answer would be appreciated. – Cheshar Jul 18 '18 at 15:45
  • @Cheshar There is a (negative) `int` whose bit pattern is `0x80000000`. But the literal `0x80000000` is not that number (after all, it's clearly not negative, and the `int` with that bit pattern is negative). If you're looking for references, read the "Integer constants" section of the C standard. – Sneftel Jul 18 '18 at 15:52
  • @Sneftel: Yes, I looked at "6.4.4.1 Integer constants" & it talks about signed and unsigned but not negative numbers. Now, I think I see a contradiction here. "There is a (negative) int whose bit pattern is 0x80000000...literal 0x80000000 is not that number (after all, it's clearly not negative". No one says 0x80000000 has to be negative. What made you deduce that? Going by the same logic, 0x80000000 just represents a negative (int). And since 0x80000000 is perfectly capable of fitting into 32 bits why can't an "int" represent it? What am I missing here? – Cheshar Jul 18 '18 at 16:09
  • @Cheshar Of course 0x80000000 is positive. It's the number 2147483648. That number is greater than zero. It's capable of fitting into an unsigned integer of 32 bits, but it is not capable of fitting into a signed integer of 32 bits. I think what might be confusing you, is that C treats integer constants as *numbers*, not bit patterns. Example: `float f = 0x1` will set `f` to 1.0f, not to the floating point number with bit pattern `0x1`. – Sneftel Jul 18 '18 at 16:19
  • @Sneftel I am not sure if I understood your last comment. But then how does one define a negative hexadecimal constant? Because clearly "-0x80000000" is an expression as you mentioned. Now assuming that int as 32 bits wide and long int as 64 bits wide, the type of "-0x800000000" (has 8 zeroes) would be long int right? – Cheshar Jul 18 '18 at 16:28
  • 1
    @Cheshar C does not have negative hexadecimal constants. (Or negative decimal constants!) If one needs the number negative three, one applies the negation operator `-` to the integer constant `3`. In your example, 0x800000000 is a `long int` (because it's too long to be an `int` or an `unsigned int`), and its negation has the same type. – Sneftel Jul 18 '18 at 16:35
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/176266/discussion-between-cheshar-and-sneftel). – Cheshar Jul 18 '18 at 16:43
  • @Sneftel Thank you! I think it makes a lot of sense now when you put it that way! But again, I don't suppose the C Standard puts "C does not have negative hexadecimal constants. (Or negative decimal constants!)" this explicitly in the specification, right? I guess all the Specifications have this uncanny knack of beating around the bush. – Cheshar Jul 18 '18 at 16:57
  • @Cheshar They can be difficult to leverage, yeah. Remember, they're building up the language from first principles, rather than giving facts about it. They want you to entirely clear your mind, and then they will give you All The Rules. Which is useful for writing a compiler, but not great for understanding individual pieces of code. – Sneftel Jul 19 '18 at 08:51
  • @Sneftel I think you are right. But then I believe, the standards would have been much easier to comprehend if they give examples along the way with steps, for readers to put concepts into perspective. Because even a seemingly innocuous expression like "-0x800000000" invokes multiple parts of the Spec like, Evaluating an integer constant expression, integer promotions(if any), applying unary minus operator, integer conversion, etc. But I guess then it's not something we can control. :) – Cheshar Jul 19 '18 at 10:04
0

One way to fix this is to use a type that you know it is likely to be able to represent and retain your value correctly, which means that your expression can be fixed like so

assert(1 > -0x80000000L);

or

assert(1 > -0x80000000LL);

Which is basically about using standard suffix in C++ for your supposedly integer expression.

The only 3 standard suffix for integer types in C++ are u, l and ll, along with the uppercase variations that mean the same thing as their lowercase counterpart; U, L and LL.

user2485710
  • 9,451
  • 13
  • 58
  • 102