Skip navigation.

Syndicate

Syndicate content

User login

Worst C/C++ Gotcha Yet

Today I ran smack into what is easily the nastiest C/C++ gotcha of my entire software engineering career. From the early 90s reading Sam’s Teach Yourself C in 21 Days with a shitty freeware C compiler from a local BBS, through to today, I have been bit by just about every imaginable C and C++ gotcha, but this one takes the cake.

If you see what’s wrong with this code, you’ve been bitten by this before:

UINT32 x = 0x80000000;
UINT32 y = 2;
UINT64 z = x * y;
cwout << L"x*y=" << std::hex << z << std::endl;

If you think this code will output 0x100000000, you’ve not been bitten by this before.

You see, 2 times 0x80000000 is 0x100000000, which is 2^32 in hex. Unfortunately, since both x and y are 32-bit unsigned integers, the result is a 32-bit unsigned integer as well, implicitly cast to a 64-bit unsigned integer only after the computation is performed. And since 0x100000000 is too big to represent as a 32-bit quantity, the low 32 bits of the value (or, 0x00000000) are all that’s preserved. That’s right; this code prints 0 for the value of z.

What’s worse is I can compile this code using Microsoft Visual C++ 2008 with warnings cranked up to max, and the compiler has absolutely nothing to say. No friendly warnings about possible truncation, nothing. And this from a compiler that won’t let a constant in a conditional slip by with without making a snide remark.

In case you’re wondering, the correct way to implement this multiplication is to cast one or both of the 32-bit integers to 64-bit, which will trigger implicit type promotion and treat the whole expression as the product of two 64-bit integers.

It’s also worth noting that the equivalent C# code:

uint x = 0x8000000;
uint y = 2;
ulong z = x * y;
Console.Out.WriteLine("x*y={0:x}", z);

produces the correct result without explicit casting. Reason number 0xbadc0de why C# is better than C/C++.