0

Can anyone please explain why this happens ?? :

int a = 2147483647;
cout <<"Product = " << a * a << endl; // output = 1 (why?)
int b = -2147483648;
cout <<"Product = " << b * b << endl; // output = 0 (why?)

Also when we write the similar for 'short' , the compiler takes the product as integer type despite the variables being initialized as of short type, like:

short x = 32767;
cout <<"Product = " << x * x << endl; // Product = 1073676289
short y = -32768;
cout <<"Product = " << y * y << endl;// Product = 1073741824
einpoklum
  • 118,144
  • 57
  • 340
  • 684
  • 7
    (1) is undefined behavior. (2) is caused by [numeric promotions](https://en.cppreference.com/w/cpp/language/implicit_conversion#Numeric_promotions). – HolyBlackCat Nov 30 '21 at 09:33
  • https://stackoverflow.com/questions/29235436/c-integer-overflow – Jose Manuel de Frutos Nov 30 '21 at 09:39
  • 1
    For comparison, 4 bits: 0b0111 * 0b0111 = 7 * 7 = 49 = 0b110001 -> 0b0001 cut down 4 bits – with more bits looks similar. With 0b1000 representing the minimum negative value it gets even more obvious how the most significant bits get shifted out of the valid range of four bits... This is what is happening *technically*, still it remains undefined behaviour! – Aconcagua Nov 30 '21 at 09:43
  • The second does rely on `int` being at least 32-bit. But that is not guaranteed by the standard. `int` is not guaranteed able to represent values more than `32767` or less than `-32767`. – Peter Nov 30 '21 at 10:17
  • @Peter -- somewhat obscure point, but that `-` in the second is an operator, not a part of the integer constant. The constant is 2147483648, and the `-` negates that value. For a compiler with 32-bit `int`, 2147483648 is too large for an `int`, so its type is `long` or `long long`, so the type of -2147483648 is also `long` or `long long`. For a twos-complement implementation, the value of `INT_MIN` can't be written directly; it has to be calculated, typically by `-INT_MAX - 1`. – Pete Becker Nov 30 '21 at 14:21

2 Answers2

8

Part 1: Intuition, without consulting references

Let's try to use some intuition, or reasonable assumptions, regarding why these results can make sense.

Basically, this is about what physical integer variables can represent - which isn't the entire infinite set of all integers, but rather the range -2^31 ... 2^31 - 1 (for a 32-bit integer, which is likely what you have; but always check to make sure, e.g. using sizeof().)

Now, if

a = 2^31 - 1

then

a*a = (2^31 - 1)^2 = 2^62 - 2*2^31 + 1 = 2^62 - 2^32 + 1

as an ideal integer, and ignoring the physical representation. When placing the result an integer with 32 bits, a reasonable guess would be that any result the CPU gives you will be equivalent, modulo 2^31 or 2^32, to the ideal result:

  (2^62 - 2^32 + 1) mod 2^32
= (2^62 mod 2^32) - (2^32 mod 2^32) + (1 mod 2^32) 
= 1 mod 2^32

which is what you're getting.

For b, you have:

b = -2147483648 = -2^31

and

b*b = 2^62

which is equivalent to 0, modulo either 2^31 or 2^32.


Part 2: What the language mandates

While our intuition does give a reasonable explanation of the results, it's usually also useful to consider the "official" answer. In our case, I'm quoting from cppreference.com (which is not official language standard, but close enough):

When signed integer arithmetic operation overflows (the result does not fit in the result type), the behavior is undefined: it may wrap around according to the rules of the representation (typically 2's complement), it may trap on some platforms or due to compiler options (e.g. -ftrapv in GCC and Clang), or may be completely optimized out by the compiler.

So actually, you're not guaranteed to get those values on a different compiler, different compiler version, different machine or even on repeat compilation of the same code! You could actually get 1234 and 5678 as the results and you couldn't complain. Or your program could crash. Or anything else could happen! See:

Undefined, unspecified and implementation-defined behavior

About the second part of your question (regarding the shorts) - this is due to integer promotion:

The arguments of the following arithmetic operators undergo implicit conversions for the purpose of obtaining the common real type, which is the type in which the calculation is performed:

  • binary arithmetic *, /, %, +, -

...

Integer promotion is the implicit conversion of a value of any integer type with rank less or equal to rank of int or of a bit field of type ...short [other types here] to the value of type int or unsigned int

Part 3: Practical bottom line

Take care to avoid computing values exceeding std::numeric_limits<T>::max(): Use a larger type to begin with, or check for excessively high values (e.g. ensure both values are lower than 2^16 in absolute value, and otherwise apply some special handling).

einpoklum
  • 118,144
  • 57
  • 340
  • 684
  • The interesting part about is that you could have replaced 32 with some general n and it would still have worked – e. g. for 64 bits (long long)... – Aconcagua Nov 30 '21 at 10:15
  • @Aconcagua: Well, not for OP's particular numeric values, but yes – einpoklum Nov 30 '21 at 12:36
-1

Can anyone please explain why this happens ?? :

int a = 2147483647;
cout <<"Product = " << a * a << endl; // output = 1 (why?)

Because the behaviour of the program is undefined.

int b = -2147483648;
cout <<"Product = " << b * b << endl; // output = 0 (why?)

Because the behaviour of the program is undefined.

Also when we write the similar for 'short' , the compiler takes the product as integer type despite the variables being initialized as of short type, like:

All operands of rank lower than int in integer arithmetic operations are implicitly converted to int (if it can represent all values of the source type; otherwise the conversion is to unsigned int). Such conversion is called integer promotion.

eerorika
  • 232,697
  • 12
  • 197
  • 326