当我在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中,字节的位移与字节一样?
答案 0 :(得分:4)
这恰好是因为byte
在执行按位操作之前是promoted到int
。 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
,所有最高位在移位之前被清除,因此结果按预期产生。
答案 1 :(得分:2)
byte
,short
和char
的转移运算符始终在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)
对每个操作数分别执行一元数字提升(第5.6.1节)。
并在5.6.1 Unary Numeric Promotion 中:
如果操作数是编译时类型byte,short或char,则通过扩展原语转换将其提升为int类型的值
因此,您的byte
个操作数会在转移前升级为int
。值-128
为11111111111111111111111110000000
。
在移位7或8次之后,最低的8位都是1,当分配给byte
时,发生缩小的基元转换。请参阅JLS 5.1.3 Narrowing Primitive Conversion :
将有符号整数缩小为整数类型T只会丢弃除 n 最低位之外的所有位,其中n是用于表示类型T的位数。