So I have about this code:
uint32_t s1 = 0xFFFFFFFFU;
uint32_t s2 = 0xFFFFFFFFU;
uint32_t v;
...
v = s1 * s2; /* Only need the low 32 bits of the result */
In all the followings I assume the compiler couldn't have any preconceptions on the range of s1
or s2
, the initializers only serving for an example above.
If I compiled this on a compiler with an integer size of 32 bits (such as when compiling for x86), no problem. The compiler would simply use s1
and s2
as uint32_t
typed values (not being able to promote them further), and the multiplication would simply give the result as the comment says (modulo UINT_MAX + 1
which is 0x100000000 this case).
However if I compiled this on a compiler with an integer size of 64 bits (such as for x86-64), there might be undefined behavior from what I can deduce from the C standard. Integer promotion would see uint32_t
can be promoted to int
(64 bit signed), the multiplication would then attempt to multiply two int
's, which, if they happen to have the values shown in the example, would cause an integer overflow, which is undefined behavior.
Am I correct with this and if so how would you avoid it in a sane way?
I spotted this question which is similar, but covers C++: What's the best C++ way to multiply unsigned integers modularly safely?. Here I would like to get an answer applicable to C (preferably C89 compatible). I wouldn't consider making a poor 32 bit machine potentially executing a 64 bit multiply an acceptable answer though (usually in code where this would be of concern, 32 bit performance might be more critical as typically those are the slower machines).
Note that the same problem can apply to 16 bit unsigned ints when compiled with a compiler having a 32 bit int size, or unsigned chars when compiled with a compiler having a 16 bit int size (the latter might be common with compilers for 8 bit CPUs: the C standard requires integers to be at least 16 bits, so a conforming compiler is likely affected).
int
is 32 bits even on most modern 64-bit architectures en.wikipedia.org/wiki/64-bit_computing#64-bit_data_models – Multiply__STDC__
is. – Crossexamineuint16_t s1,s2
on 32-bitint
machines. (anduint8_t s1,s2
on 16-bitint
machines.) Also with other orators like '+', '-' and maybe '/', '%'. – TynanF U!
" ... "Yeah? Well then. FFFFFFFFU!
" – Plenteousint
code begin ported to 32-bitint
. It's the same problem, just different sizes. – Tynanushort3=ushort1*ushort2;
should yield the "natural" (mod-65536) result absent some reason why it shouldn't, but the hyper-modern philosophy is that compilers are entitled to use the fact thatushort1*ushort2
"can't" be bigger than INT_MAX to prune any code paths which would only occur if the product were larger thanINT_MAX
. – Scuppernong