Reputation: 36906
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
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