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