Tricky C Code: Unexpected Optimization Effect

At first, C seems like a much simpler language compared to C++. But, it is actually quite deep and complex. Today, I want to share some interesting facts about type promotion and optimization in C. This is not a tutorial, just some fascinating information about the C language.

#include <stdio.h>

void trickyFunction(unsigned short a, unsigned short b)
{

        unsigned int x;
        x = a * b;

        if (x >= 0x80000000)
                printf("%u >= %u\n", x, 0x80000000);
        else
                printf("%u < %u\n", x, 0x80000000);
}

int main()
{
        trickyFunction(65535, 65535);
        return 0;
}

The code above is really simple: we multiply two unsigned short values, store the result in an unsigned int, and then compare it with the value 2147483648 (0x80000000). It looks quite simple and obvious. The result of 65535 x 65535 is 4,294,836,225. So, what output do we expect from this code?

Can 4,294,836,225 be smaller than 2147483648? Of course not. So, let’s compile the code and run it.

I have this code in a main.c file, and I compile it with GCC using the command gcc main.c -o app. And when I run ./app, output looks like :

4294836225 >= 2147483648

Nothing weird, everytings is simple and correct.

However, let’s assume that we want GCC to optimize our code. Why not have more optimized code, right? We recompile the code with the optimization flag using gcc main.c -o appOptimized -O1, and then we run it with ./appOptimized. And let’s see the output :

4294836225 < 2147483648

Now, something seems strange. How can 4,294,836,225 be smaller than 2,147,483,648? We just wanted to optimize the code, but it seems we accidentally broke it.

But things get interesting in the if statement.When optimization is enabled, compiler will cast a and b variables to int because they are unsigned short. Then compiler thinks that since x is obtained from two int multiplications, it can not be bigger than the maximum positive value of an int. This maximum value is 2147483647, which is obviously smaller than 2147483648 (0x80000000), so the compiler decides to remove the first if part.

Solution is to cast our parameters ourselves instead of leaving it to the compiler.

x = (unsigned int)a * (unsigned int)b;

This will guarantee the proper calculation at any optimization level.

I learned this tricky information from an amazing tutorial : here . I highly recommend you watch it.

Leave a comment