Bitwise right shift operator in Java

2019-06-26 04:23发布

问题:

In Java, -4 >> 2 gives -1 but -5 >> 2 gives -2. Can anybody explain why? Here is a sample code:

byte r=-5;
r>>=2;
System.out.println(r);

Also in this scenario >> and >>> operators give the same answer. Can anyone explain that as well?

回答1:

You can take a look at the bits. Using two's complement notation, the bits for -4 and -5 are, showing only the last 8 bits for brevity:

-4: 1111 1100      
-5: 1111 1011

Bit shifting to the right 2 positions, with sign extension:

-4 >> 2: 1111 1111 (-1)
-5 >> 2: 1111 1110 (-2)

Normally, you think of >>> not using sign extension, and this is true, but in this case:

r >>>= 2;

... the value r is promoted to int for the bit-shift operation using binary numeric promotion, but the compound assignment operator casts the returned value back to byte, and the shifted-in zero "disappears".

byte r = -5;     // 1111 1100
r >>>= -2;       // promoted to int:   11111111 11111111 11111111 11111010
                 // bit shift:         00111111 11111111 11111111 11111110
                 // cast back to byte: 11111110 (-2)

The JLS, Section 15.26.2, talks about the casting operation done in compound assignment operators:

A compound assignment expression of the form E1 op= E2 is equivalent to E1 = (T) ((E1) op (E2)), where T is the type of E1, except that E1 is evaluated only once.

That is, in this case, the result of the bit-shift is casted back to byte.

The same cast-back-to-byte operation occurs when the value of r is -4.

Note that if the assignment part wasn't done, then you wouldn't see the same answer, because it wouldn't cast the result back to byte:

System.out.println(r >>> 2);

Then you would see:

1073741822


回答2:

Can anybody explain why (-4 >> 2 is -1, and -5 >> 2 is -2)?

Binary -4 byte looks like this: 11111100. When you shift it right while extending the sign, you get 11111111 (the ones on the left is sign extension, i.e. keeping the sign bit while shifting left). This is -1 in two's complement representation.

Binary -5 byte looks like this: 11111011. When you shift it right while extending the sign, you get 11111110 (again, the ones on the left is sign extension). This is -2.

>> and >>> operators give the same answer. Can anyone explain that as well?

Shifting is performed in integers. When you use >>> to shift -4 without sign extension, you get 00111111111111111111111111111111. When you convert it to byte, you chop off the top 24 bits, ending up with the lower eight bits all set to 1. Same goes for >>> of -5.



回答3:

In the case of negative numbers, in memory they are stored using a technique called two's complement. In this example, 5 is: 0...0101 (i am removing a lot of 0's for sake of length so when you shift 0101 twice you get:

0101 >> 0010
0010 >> 0001

0001 is 1

in two's complement you have 1...1011 (inverse all the bits then add a binary 1) so when you shift 1011 you have

1011 >> 1101 (we carry the other 1's)
1101 >> 1110 (we carry the other 1's)

1...1110 is the negative for -2



回答4:

Java uses the two's complement format to represent negative numbers.

-5 is represented as 11111011 in binary which when right-bitshifted 2 bits over becomes 11111110, which is -2. Similarly, -4 is represented as 11111100, which when right-bitshifted by 2 becomes 11111111, which is -1 in decimal.

The reason >> and >>> appear to be the same is because the >>> operator is a logical right shift on integers, not bytes. Therefore java is type casting r to an int before it preforms the logical shift and then truncating the bitstring to only the least significant 8 bits. Because the integer representation of -5 is 11111111111111111111111111111011 and the logical shift 2 right is 11111111111111111111111111111110 which is typecast to a byte of 11111110, which is -2.

If you try the following code you see the results:

byte r = -5;
System.out.println(r >> 2);
System.out.println(r >>> 2);
r>>>=2;
System.out.println(r);


回答5:

The byte -4 == 0xFC. If you shift that arithmetically right (i.e. preserving the sign bit) by 2 bits you get 0xFF, which is -1.

-5 == 0xFB. If you shift that arithmetically right by 2 bits you get 0xFE, which is -2.