-3

I want to understand more about int overflow in C++ on a 64-bit system. So I would like to stick to int for my understanding without casting / extending the type to unsigned or 64-bit and I am on Intel x64 Ubuntu using VSCode with cmake.

$ g++ --version
g++ (Ubuntu 14.2.0-4ubuntu2) 14.2.0
Copyright (C) 2024 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

The following just shows assertion failed without the -Woverflow exception:

int i = 0x80000000;
cout << "i: " << i << ", -i: " << -i << endl; // Output: i: -2147483648, -i: -2147483648
assert(0x80000000 == -0x80000000); // OK
assert(i == -i); // XXX: assert exception. Why!?!
/*
 * 0x8000_0000 + 1 = -0x7FFF_FFFF = 0x8000_0001 (Two's compliment)
 * 0x8000_0000 - 1 = -0x8000_0001 = 0x7FFF_FFFF
 */
int i = 0x80000000;
int j = i + 1;
cout << i << " (0x" << hex << i << ") + 1 = " << dec << j << " 0x" << hex << j << endl;
assert(j == 0x80000001); // OK
j = i - 1;
cout << dec << i << " (0x" << hex << i << ") - 1 = " << dec << j << " 0x" << hex << j << endl;
assert(j == 0x7FFFFFFF); // XXX: assert exception. Why!?!

The following throws -Woverflow exception:

int i = 0x80000000; // numeric_limits<int>::min()
int j = 0x7FFFFFFF; // numeric_limits<int>::max()
cout << i << " - " << j << " = " << i - j << endl; // Output: "-2147483648 - 2147483647 = 1"
assert(1 == i - 0x7FFFFFFF); // XXX: integer overflow in expression of type ‘int’ results in ‘1’ [-Woverflow]
assert(1 == (i - j)); // XXX: integer overflow in expression of type ‘int’ results in ‘1’ [-Woverflow]
i = -INT_MIN; // == 0x80000000
assert(j == (i - 0x7FFFFFFF)); // XXX: integer overflow in expression of type ‘int’ results in ‘1’ [-Woverflow]

These assertions pass:

assert(1 == (int)0x80000000 - (int)0x7fffffff);
assert(1 == 0x80000000 - 0x7fffffff);

Here are my questions: (1) Why would 0x80000000 - 0x7FFFFFFF overflow? Shouldn't it rightly yield 1? (2) Why is the cout behaviour different from assert? (3) Why do some throws assert exception and some -Woverflow exception?

8
  • 5
    Expressions that produce overflow of signed integral types in C++ gives undefined behaviour according to the standard. So there is no "right" result. Commented Feb 19 at 4:04
  • 2
    0x80000000 is a minimal negative 32 bit value. If you substract any positive number then you get integer overflow. Commented Feb 19 at 4:04
  • 1
    int i=0x80000000; int j=0x7fffffff; i-j is not the same as 0x80000000-0x7fffffff. The former results in a signed overflow due to the declared types of i and j, the latter is perfectly valid operation between two unsigned integers. Your first question is asking the wrong question. Commented Feb 19 at 4:47
  • 1
    @khteh That is UB. It just happens to pass. -Woverflow may be designed to work only with expressions containing variables, but that's very compiler-specific. Commented Feb 19 at 5:03
  • 1
    The nature of undefined behaviour (as per the standard) is that the standard does not impose any constraints or requirements on the result. When behaviour is undefined, any result - whether it makes sense to the programmer or not - is allowed. The evaluation (assuming a 32-bit int) of (int)0x80000000 - (int)0x7fffffff) gives undefined behaviour, so assert(1 == (int)0x80000000 - (int)0x7fffffff) is allowed to pass, it is allowed to fail, or [fortunately, rare in practice] your compiler is permitted to reformat your hard drive. Commented Feb 19 at 5:20

2 Answers 2

2

The moment you have signed integer overflow you have UB in your code and anything can happen (the whole program becomes invalid and you can no longer trust ANY result). Or in other words UB is undetectable by code so you cannot check for it from code. Side note : When checking for signed integer overflow (UB) make sure you use the std::int64_t or std::int32_t so you know what you get (for int you don't know).

Sign up to request clarification or add additional context in comments.

2 Comments

This only gives you the assurance in the IDE that it is typedef to32-bit. I can also check the same information for int using sizeof and numeric_limits<int>. So, it doesn't quite explain and answer the question.
@khteh You miss my main point, by trying to test signed integer overflow the code has UB. And because the code has UB you cannot trust any of its outputs! So you cannot write code that tests how integer overflow behaves it is meaningless.
1

int follows two's complement arithmetic. In this arithmetic, the sign is the most significant bit and negative values have bits reversed. So

0x00000000 =  0
0xFFFFFFFF = -1
0x7FFFFFFF =  2^15-1 (maximum value)
0x80000000 = -2^15   (minimum value)

The particularity of two's complement is when you add 1, you will get the right sign except at the maximum value, where adding one makes all bits change value, including the sign bit. When this happens, we say it is an overflow and is undefined behaviour in C++.

The same is true when substracting 1, but this time when you substract 1 to the minimum value, all the bits flip to the maximum value. This is also an overflow. Any values above 1 result in an overflow in both cases.

3 Comments

How about the assert(i == -i); assertion error?
@khteh That's the UB part of the code. Since there is UB it can produce any output. E.g. the final executable may or may not assert... anything goes from here
@khteh If you are really interested in going beyond simply "This is UB", godbolt.org/z/q98TxK4M7 shows that without any explicit optimization flags (which you didn't give in the question), assert(i == -i); is optimized to assert(i == 0);. In general, these behavior are very sensitive to compiler version and compiling flags and an answer for a particular setup is not particular useful.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.