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
CodePudding user response:
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
i.e.
b = -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 short
s) - 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 typeint
orunsigned int
Part 3: Practical bottom line
Take care to avoid computing values 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).
CodePudding user response:
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.