As I was using bit-shifting on byte
, I notice I was getting weird results when using unsigned right shift (>>>
). With int
, both right shift (signed:>>
and unsigned:>>>
) behave as expected:
int min1 = Integer.MIN_VALUE>>31; //min1 = -1
int min2 = Integer.MIN_VALUE>>>31; //min2 = 1
But when I do the same with byte
, strange things happen with unsigned right shift:
byte b1 = Byte.MIN_VALUE; //b1 = -128
b1 >>= 7; //b1 = -1
byte b2 = Byte.MIN_VALUE; //b2 = -128
b2 >>>= 7; //b2 = -1; NOT 1!
b2 >>>= 8; //b2 = -1; NOT 0!
I figured that it could be that the compiler is converting the byte
to int
internally, but does not seem quite sufficient to explain that behaviour.
Why is bit-shifting behaving that way with byte in Java?
Shift operators for
byte
,short
andchar
are always done onint
.Therefore, the value really being shifted is the
int
value-128
, which looks like thisWhen you do
b2 >>= 7;
what you are really doing is shifting the above value 7 places to the right, then casting back to abyte
by only considering the last 8 bits.After shifting 7 places to the right we get
When we convert this back to a byte, we get just
11111111
, which is-1
, because thebyte
type is signed.If you want to get the answer 1 you could shift 31 places without sign extension.
This happens exactly because
byte
is promoted toint
prior performing bitwise operations.int -128
is presented as:Thus, shifting right to 7 or 8 bits still leaves 7-th bit 1, so result is narrowed to negative
byte
value.Compare:
By
b & 0xFF
, all highest bits are cleared prior shift, so result is produced as expected.Refer to JLS 15.19 Shift Operators:
and in 5.6.1 Unary Numeric Promotion :
So, your
byte
operands are promoted toint
before shifting. The value-128
is11111111111111111111111110000000
.After the shifting 7 or 8 times, the lowest 8 bits are all 1s, which when assigning to a
byte
, a narrowing primitive conversion occurs. Refer to JLS 5.1.3 Narrowing Primitive Conversion :