Java 中字节移位的奇怪行为

Weird behaviour of bit-shifting with byte in Java

当我在 byte 上使用位移位时,我注意到在使用无符号右移 (>>>) 时得到了奇怪的结果。对于 int,右移(有符号:>> 和无符号:>>>)都按预期运行:

    int min1 = Integer.MIN_VALUE>>31; //min1 = -1
    int min2 = Integer.MIN_VALUE>>>31; //min2 = 1

但是当我对 byte 做同样的事情时,无符号右移会发生奇怪的事情:

    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!

我认为可能是编译器在内部将 byte 转换为 int,但似乎不足以解释该行为。

为什么 Java 中的字节移位会这样?

byteshortchar 的移位运算符始终在 int 上完成。

因此,真正被移动的值是 int-128,看起来像这样

int b = 0b11111111_11111111_11111111_10000000;

当您执行 b2 >>= 7; 时,您真正在做的是将上述值向右移动 7 位,然后仅考虑最后 8 位就转换回 byte

右移7位后得到

        0b11111111_11111111_11111111_11111111;

当我们将其转换回字节时,我们只得到 11111111,即 -1,因为 byte 类型已签名。

如果你想得到答案 1 你可以移动 31 个位置而不用符号扩展。

byte b2 = Byte.MIN_VALUE; //b2 = -128
b2 >>>= 31;
System.out.println(b2);   // 1

这正是因为 bytepromotedint 在执行按位运算之前。 int -128 显示为:

11111111 11111111 11111111 10000000

因此,右移7位或8位仍然保留第7位1,所以结果是narrowed负值byte

比较:

System.out.println((byte) (b >>> 7));           // -1
System.out.println((byte) ((b & 0xFF) >>> 7));  //  1

通过b & 0xFF,所有最高位都在移位之前被清除,因此结果如预期的那样产生。

参考JLS 15.19 Shift Operators:

Unary numeric promotion (§5.6.1) is performed on each operand separately.

并在 5.6.1 Unary Numeric Promotion 中:

if the operand is of compile-time type byte, short, or char, it is promoted to a value of type int by a widening primitive conversion

因此,您的 byte 操作数在移位之前被提升为 int。值 -12811111111111111111111111110000000 .

移位7、8次后,最低8位全为1,赋值给byte时,会发生变窄原语转换。参考JLS 5.1.3 Narrowing Primitive Conversion :

A narrowing conversion of a signed integer to an integral type T simply discards all but the n lowest order bits, where n is the number of bits used to represent type T.