skip to Main Content

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


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

    Login or Signup to reply.
  2. uint16_t x = 0xFFFF;
    
    // 1. wrong
    uint64_t pow = x*x;
    
    // 2. right
    uint64_t pow = (uint64_t)x*x;
    

    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 to int, and then the multiplication is performed. This results in undefined behavior signed integer overflow, which is a bug.

    In 2, we first cast x to uint64_t. Then, the multiplication is performed on two uint64_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:

    By default, MATLAB® stores all numeric variables as double-precision floating-point values.

    See also

    This topic comes up a lot, so, I’ll link to a lot of other relevant answers on this topic here:

    1. Why is unsigned integer overflow defined behavior but signed integer overflow isn’t?

    2. Integer and floating point rank and promotion rules in C and C++

      1. @Lundin’s answer

      2. my summary answer

        From the bottom of my answer there:

        1. https://cppinsights.io/ – a very useful tool which expands your C++ code into exactly what the compiler sees, including after applying all automatic implicit type promotion rules in the compiler.
          1. Ex: see my code above here: https://cppinsights.io/s/bfc425f6 –> then click the play button to convert and expand it into what the compiler sees.
    3. My answer: Adding int type to uint64_t c++

    4. My answer: In C , why does this main function with an incrementing integer never crash from overflow?

    5. My answer: How could I safely find the absolute difference between 2 signed integers in C?

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search