Could someone explain why pow variable is set to -131071 (0xfffffffffffe0001) in this example?
uint16_t x = 0xFFFF;
uint64_t pow = x*x;
I know it can be fixed by casting x to uint64_t, but my question is why it gives wrong result without a cast.
gcc --version
gcc (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0
#include <stdio.h>
#include <stdint.h>
int main() {
uint16_t x = 0xFFFF;
uint64_t pow = x*x;
printf("%ldn", pow);
return 0;
}
2
Answers
That must be because it’s first multiplied and then cast.
The reference for the C17 standard (the default for the latest GCC) must have specific provisions statng that, but the paper is ~$230 which is more than I’m willing to pay to post a StackOverflow answer.
In 1, since
x
is a "small integer type" (see @Lundin’s answer here and my answer here for more on that, it first gets promoted toint
, and then the multiplication is performed. This results in undefined behavior signed integer overflow, which is a bug.In 2, we first cast
x
touint64_t
. Then, the multiplication is performed on twouint64_t
values, which is well-defined whether it overflows or not (but it won’t overflow in this case), and we get the correct result.These types of "gotchas" are very tricky in C and C++, and cause a lot of people (myself included) a lot of frustration when trying to do math in C or C++. You have to study these gotchas and take care to do it correctly. A lousy, (temporary, or else your code bloats) work-around is to just use
double
for everything, which is what computation-intensive languages like MATLAB do. See: https://www.mathworks.com/help/matlab/data-types.html:See also
This topic comes up a lot, so, I’ll link to a lot of other relevant answers on this topic here:
Why is unsigned integer overflow defined behavior but signed integer overflow isn’t?
Integer and floating point rank and promotion rules in C and C++
@Lundin’s answer
my summary answer
From the bottom of my answer there:
My answer: Adding int type to uint64_t c++
My answer: In C , why does this
main
function with an incrementing integer never crash from overflow?My answer: How could I safely find the absolute difference between 2 signed integers in C?