2

I understand the results of p. Could someone please explain why up2 (uint64_t type) != 2147483648 but up (uint32_t type) == 2147483648 ?

Some mention that assigning -INT_MIN to unsigned integer up2 will cause overflow, but

  1. -INT_MIN is already a positive number, so it is fine to assign it to uint64_t up2?

  2. Why it seems to be ok to assign -INT_MIN to uint32_t up? It produces correct result as 2147483648.

    #include <iostream>
    #include <climits>
    
    using namespace std;
    
    int main() {
        int n = INT_MIN;
        int p = -n;
        uint32_t up = -n;
        uint64_t up2 = -n;
    
        cout << "n: " << n << endl;
        cout << "p: " << p << " up: " << up << " up2: " << up2 << endl;
        return 0;
    }
    

    Result:

    n: -2147483648
    p: -2147483648 //because -INT_MIN = INT_MIN for signed integer
    up: 2147483648 //because up is unsigned int from 0 to 4,294,967,295 (2^32 − 1) and can cover 2147483648
    
    up2: 18446744071562067968  //Question here. WHY up2 != up (2147483648)???
    
phuclv
  • 37,963
  • 15
  • 156
  • 475
thinkdeep
  • 945
  • 1
  • 14
  • 32

4 Answers4

7

The behaviour of int p = -n; is undefined on a 2's complement system (accepting that you have a typo in your question; INT_MAX is always odd on such a system), due to your overflowing an int type. So your entire program is undefined.

This is why you see INT_MIN defined as -INT_MAX - 1 in many libraries.

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
  • 6
    While correct this does not address OP's question about the value for `up2` – Govind Parmar Jan 21 '19 at 14:42
  • 2
    Isn't just undefined? If you know the system is two's-compliment then it wouldn't be undefined because you know how it will behave? – NathanOliver Jan 21 '19 at 14:42
  • @Bathsheba Could you please explain the up2 ? – thinkdeep Jan 21 '19 at 14:44
  • 1
    @coder It's undefined. The `n` in `-n` is an int ,so it will overflow. The same evaluation on `-n` is performed in `uint64_t up2 = -n` and in `int p = -n`; The type (int or uint64) of the variable you store the result in does not affect the evaluation of the subexpression `-n`. (But as this is undefined behavior, you could get different result that does or does not make sense) – nos Jan 21 '19 at 14:45
  • 4
    @coder: Bad things will happen to me if I try to explain something that's undefined. – Bathsheba Jan 21 '19 at 14:45
  • @nos but why assigning `-n` to `uint32_t up` works? – thinkdeep Jan 22 '19 at 09:40
  • 1
    @coder It does not "work". It is undefined behavior. Just because it shows the number you expected does not mean it is without issues. You could assign -n to uint32_t like that another place in the code, or with other options to your compiler , or with another version of the compiler etc., and it could give you another value. – nos Jan 22 '19 at 09:46
4

Note that while you invoke undefined behavior due to signed integer overflow, here is the most likely explanation for the behavior you are observing:

If int is 32 bits on your system, and your system uses one's complement or two's complement for signed integer storage, then the sign bit will be extended into the upper 32-bits of a 64 bit unsigned type.

It may make more sense if you print out your values in base-16 instead.

n = 0x80000000
p = 0x80000000
up = 0x80000000
up2 = 0xFFFFFFFF80000000
Govind Parmar
  • 20,656
  • 7
  • 53
  • 85
  • this is dangerous because it could change behavior at any time. Code around it changing could change the behavior. Changing compilers could change it. Changing the environment you're compiling for could change it. – xaxxon Apr 30 '21 at 00:59
  • @xaxxon Like I said, OP has undefined behavior. My answer only delved into the specifics on his system. – Govind Parmar Apr 30 '21 at 15:39
2

What you see is -n converted to an uint64, where the overflow is not on 4 billion, but 2**64:

18446744073709551616 - 2147483648 = 18446744071562067968
xaxxon
  • 19,189
  • 5
  • 50
  • 80
Matthieu Brucher
  • 21,634
  • 7
  • 38
  • 62
1

The expression -n in your case causes undefined behavior, since the result cannot fit into the range of int data type. (Whether or not you assign this undefined result to a variable of "wider" type doesn't matter at all, the inversion itself is made with int.)

Trying to explain undefined behavior makes no sense.

Daniel Langr
  • 22,196
  • 3
  • 50
  • 93
  • 3
    It's likely to be dangerous and/or unhelpful for solving a given problem to try and explain UB, but (especially without compiler optimizations) one may still learn something from understanding why a given behavior manifests. – Max Langhof Jan 21 '19 at 15:00