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
5
  • 7
    (1) is undefined behavior. (2) is caused by numeric promotions. Commented Nov 30, 2021 at 9:33
  • stackoverflow.com/questions/29235436/c-integer-overflow Commented Nov 30, 2021 at 9: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! Commented Nov 30, 2021 at 9: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. Commented Nov 30, 2021 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. Commented Nov 30, 2021 at 14:21

2 Answers 2

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).

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

2 Comments

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: Well, not for OP's particular numeric values, but yes
-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.

Comments

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.