在Java中使用字节进行位移的奇怪行为

时间:2016-04-06 14:59:24

标签: java byte bitwise-operators bit-shift binary-operators

当我在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中,字节的位移与字节一样?

3 个答案:

答案 0 :(得分:4)

这恰好是因为byte在执行按位操作之前是promotedintint -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,所有最高位在移位之前被清除,因此结果按预期产生。

答案 1 :(得分:2)

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

答案 2 :(得分:0)

请参阅JLS 15.19 Shift Operators

  

对每个操作数分别执行一元数字提升(第5.6.1节)。

并在5.6.1 Unary Numeric Promotion 中:

  

如果操作数是编译时类型byte,short或char,则通过扩展原语转换将其提升为int类型的值

因此,您的byte个操作数会在转移前升级为int。值-12811111111111111111111111110000000

在移位7或8次之后,最低的8位都是1,当分配给byte时,发生缩小的基元转换。请参阅JLS 5.1.3 Narrowing Primitive Conversion

  

将有符号整数缩小为整数类型T只会丢弃除 n 最低位之外的所有位,其中n是用于表示类型T的位数。