0

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?

tenfour
  • 36,141
  • 15
  • 83
  • 142
  • 1
    _"// Debugger calls z an 'int' with value of 0xfffffffa"_ the value can't be `0xfffffffa`, it must be `0xfffffff5`, please clarify – Jabberwocky Oct 30 '18 at 15:34
  • 6
    `z` is signed, and when coerced to the width of `x`, it is sign-extended. Don't use signed numbers for bit manipulation. – n. m. could be an AI Oct 30 '18 at 15:36
  • 2
    This ends up coming down usual arithmetic conversions and integral promotions examples [this](https://stackoverflow.com/q/22702091/1708801) and [this](https://stackoverflow.com/q/23994293/1708801) – Shafik Yaghmour Oct 30 '18 at 15:37
  • Using `auto z` is a poor idea here. Use `uint8_t z` instead or maybe `uint64_t z`, it depends on what you want to achieve. – Jabberwocky Oct 30 '18 at 15:39
  • Still not qute correct: `0xccccccccccccccc4` -> `0xccccccccccccccc5` etc. – Jabberwocky Oct 30 '18 at 15:40

1 Answers1

3

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.

Sneftel
  • 40,271
  • 12
  • 71
  • 104