tenfour
tenfour

Reputation: 36906

Why does the C++ bitwise AND operator behave like this with different-sized operands?

Curious about whether type size is a factor when removing bits using one's compliment operator ~, I ended up with this curious result:

uint64_t x = 0xccccccccccccccccUL;
uint8_t y = 10;
auto z = ~y; // Debugger calls z an 'int' with value of 0xfffffff5
x &= z;
// x is now the correct value: 0xccccccccccccccc4

How can operator & return a value greater than either of the operands?

The following example confuses me further, because it seems like the same logic but yields a different result:

uint64_t x = 0xccccccccccccccccUL;
x &= 0xfffffff5;
// Now x is 0x00000000ccccccc4

What explains this? Is it actually safe to use x &= ~y to remove bits regardless of type size / sign?

Upvotes: 0

Views: 287

Answers (1)

Sneftel
Sneftel

Reputation: 41542

You're doing two widening operations there. The first is your ~, which (like any arithmetic operator) performs standard "integer promotion". In this case, since all values of the source type (uint8_t) can fit in an int, that's the promotion performed. So as you've noticed, z is deduced to have type int.

The second widening operation is the &=, which in this case is equivalent to x = x & z. uint64_t won't fit in an int or in an unsigned int, nor even in an int64_t, but both uint64_t and int will fit in a uint64_t, so that's the chosen type. Widening a (signed) int involves sign extension, so that's where the extra bits come from.

To answer your final question, no, it is not safe to use x &= ~y to clear a bit if y has a signed type or is narrower than x. For more about that, see this article, but the bottom line is, make sure to convert ~var to the expected unsigned type if there may be a widening promotion happening. If you had used uint8_t instead of auto for z, you wouldn't have run into this issue.

Upvotes: 4

Related Questions