I encountered a strange problem, but to make it clear see the code first:
#include <stdio.h>
#include <stdint.h>
int main() {
uint8_t a = 0b1000'0000; // -> one leftmost bit
uint8_t b = 0b1000'0000;
a = (a << 1) >> 1; // -> Both shifts in one line
b = b << 1; // -> Shifts separated into two individual lines
b = b >> 1;
printf("%i != %i", a, b);
return 0;
}
(using C++ 17 on a x86 machine)
If you compile the code, b
is 0
while a
is 128
.
On a general level, this expressions should not be tied to the processors architecture or its bit width, I would expect both to be 0
after the operation
The bitshift right operator is defined to fill up the left bits with zero, as the example with b
proves.
If I look at the assembler code, I can see that for b
, the value is loaded from RAM into a register, shifted left, written back into RAM, read again from RAM into a register and then shifted write. On every write back into RAM, the truncation to 8 bit integer is done, removing the leftmost 1
from the byte, as it is out of range for an 8-bit integer.
For a
on the other hand, the value is loaded in a register (based on the x86 architecture, 32-bit wide), shifted left, then shifted right again, shifting the 1 just back where it was, caused by the 32-bit register.
My question is now, is this one-line optimization for a
a correct behavior and should be taken in account while writing code, or is it a compiler bug to be reported to the compiler developers?