3

I am currently trying to extract some bits from an address called addr with a 32 bit mask called mask into another variable called result as follows

int addr = 7;
int x = 0;
uint32_t mask = 0xFFFFFFFF;
result = addr & (mask >> (32 - x));

I am expecting result to be 0 when x = 0, and this is confirmed on online bitshift calculators. however in C code, result is 1. Why is that?

VillageTech
  • 1,968
  • 8
  • 18
DanielJomaa
  • 497
  • 6
  • 21
  • 4
    The behavior is undefined. http://port70.net/~nsz/c/c11/n1570.html#6.5.7p3 *If the value of the right operand is negative or is greater than or equal to the width of the promoted left operand, the behavior is undefined.* – Eugene Sh. Dec 11 '19 at 21:59
  • What is type of `addr`? – VillageTech Dec 11 '19 at 21:59
  • This kind of question would greatly benefit from a [mcve]. – hyde Dec 11 '19 at 22:01
  • 2
    @hyde Why? It is pretty clear without it. Should be a duplicate though – Eugene Sh. Dec 11 '19 at 22:01
  • @EugeneSh. and you're right! – Jean-François Fabre Dec 11 '19 at 22:03
  • @EugeneSh. See the edit to see what I think was missing in the first version of the Q. Asker avoids such omissions most easily by providing a [mcve]. – hyde Dec 11 '19 at 22:03
  • In case you wonder why it is undefined behaviour, consider the hardware. For example on x86 (and probably lots of others) `The destination operand can be a register or a memory location. The count operand can be an immediate value or register CL. The count is masked to 5 bits, which limits the count range to 0 to 31.` So you cannot shift by 32 bits because it is the same as shifting by 0 bits once the hardware masks the count value. – Jerry Jeremiah Dec 11 '19 at 22:14

2 Answers2

2

You're performing an illegal bitshift.

Shifting by a value greater or equal than the size in bits of the left operand results in undefined behavior. This is documented in section 6.5.7p3 of the C standard:

The integer promotions are performed on each of the operands. The type of the result is that of the promoted left operand. If the value of the right operand is negative or is greater than or equal to the width of the promoted left operand, the behavior is undefined.

This means you need to check the value of x, and if it is 0 then just use 0 for the bitmask.

int x = 0;
uint32_t mask = 0xFFFFFFFF;
...
if (x == 0) {
    result = 0;
} else {
    result = addr & (mask >> (32 - x));
}
dbush
  • 205,898
  • 23
  • 218
  • 273
  • @Clifford If `x` is 1 then we have `mask >> 31` == `0xffffffff >> 31` == 1 – dbush Dec 11 '19 at 23:16
  • I need more sleep (comment deleted)! An alternative is `addr & ((mask >> (31 - x)) >> 1)` then the check for x==0 is unnecessary (I think - still need sleep!) – Clifford Dec 12 '19 at 00:07
1

From the C standard (6.5.7 Bitwise shift operators)

3 The integer promotions are performed on each of the operands. The type of the result is that of the promoted left operand. If the value of the right operand is negative or is greater than or equal to the width of the promoted left operand, the behavior is undefined

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335