Reputation: 2972
I have a method in my C# application which is attempting to pack an integer, but the shift operator is behaving strangely.
The comments in the code below show the contents of code as seen by the debugger after the execution of the line.
long code = 0; //0x0000000000000000
code += (0x1111 << 32); //0x0000000000001111
code += (0x2222 << 16); //0x0000000022221111
code += (0x3333); //0x0000000022224444
Take the first line, I would expect that the code contained in parentheses would be executed first, which would result in 0x1111 being shifted left by 32 bits, yet it isn't, but on the next line the shift does take place, and in the correct place as well (i.e. 0x2222 must be shifted before the add otherwise the result would be 0x33330000).
What have I missed about the operator that explains this?
Upvotes: 2
Views: 159
Reputation: 86848
What you missed is that bit shift operations on ints are mod 32. Since 0x1111
is an int
, the operation is 0x1111 << (32 % 32)
, which is 0x1111 << 0
, which is just 0x1111
.
What you probably want is 0x1111L << 32
.
From section 7.9 of the C# spec:
When the type of x is
int
oruint
, the shift count is given by the low-order five bits ofcount
. In other words, the shift count is computed fromcount & 0x1F
.When the type of x is
long
orulong
, the shift count is given by the low-order six bits ofcount
. In other words, the shift count is computed fromcount & 0x3F
.
On some hardware architectures, shifting an int by 32 will yield 0, while other architectures will yield the same int. In order to have consistent behavior, the designers of C# had to choose one of those options. The current behavior simply requires performing & 0x1F
on the shift count on architectures that otherwise yield 0. However, always returning 0 would require a comparison and a branch on architectures that yield the int. Since branches tend to be expensive, it makes sense that they chose the option that is fastest in the most cases.
Upvotes: 6