As a follow-up to "https://stackoverflow.com/questions/33732041/why-static-castunsigned-intushrt-maxushrt-max-yields-correct-value"
I was asking myself if promoting all types (except some exceptions) with a lower rank than int
to int
to perform arithmetic operations might cause UB in some cases.
e.g.:
unsigned short a = 0xFFFF;
unsigned short b = a*a;
As unsigned short is promoted to int
for arithmetic operations this would result in:
unsigned short a = 0xFFFF;
unsigned short b = (int)a*(int)a;
As (int)0xFFFF*(int)0xFFFF
causes an overflow, and overflow of signed types is UB: Can multiplying two unsigned shorts x,y
cause undefined behaviour in the case that x*y > INT_MAX
UPDATE:
The question specifically aims at the case that int
is 32-bit and short
is 16-bit.
” Unsigned integers, declared
unsigned
, shall obey the laws of arithmetic modulo 2n where n is the number of bits in the value representation of that particular size of integer.
Apart from the slightly misleading wording about “declared unsigned
” this might seem to apply that every arithmetic expression that involve only argument of some given unsigned type, will yield a result modulo 2n for that type.
However, there are no arithmetic expressions at all for unsigned types of lower conversion rank than int
: all arguments in an apparent such expression are converted up to (1)at least int
, or depending on the number ranges of the C++ implementation, up to unsigned int
.
As a result, a*b
where a
and b
are unsigned short
values, (2)can have formally Undefined Behavior. Because it's not an unsigned short
expression. It's (in practice) an int
expression.
That said, with a reasonable compiler that doesn't introduce special casing where it notices formal UB, and with in-practice 8 bit bytes and unsigned short
max value that is representable by int
, and common two's complement signed integer representation, the result, when converted back down to unsigned short
, will be as if it was modular arithmetic in the range of unsigned short
. That's because two's complement, at the machine code level, is just modular arithmetic with a range centered on 0.
(1) In practice one will usually be using an 8 bits-per-byte implementation where the maximum value of unsigned short
fits well within the int
range, so in practice we're talking about a conversion up to int
.
(2) E.g., for 16-bit unsigned short
and 32-bit int
, (216−1)2 = 232−2×216+1 > 231−1, where the last value is the maximum positive int
value.