考虑以下Java代码片段
byte b=(byte) 0xf1;
byte c=(byte)(b>>4);
byte d=(byte) (b>>>4);
输出:
c=0xff
d=0xff
预期产量:
c=0x0f
怎么样?作为b在二进制1111 0001
后无符号右移0000 1111
因此0x0f
但为什么它0xff
如何?
问题是所有参数在转换操作发生之前首先被提升为int
:
byte b = (byte) 0xf1;
b
已签署,因此其值为-15。
byte c = (byte) (b >> 4);
b
首先符号扩展到整数-15 = 0xfffffff1
,然后向右移动到0xffffffff
并被施法者截断到0xff
到byte
。
byte d = (byte) (b >>> 4);
b
首先符号扩展到整数-15 = 0xfffffff1
,然后向右移动到0x0fffffff
并被施法者截断到0xff
到byte
。
你可以做(b & 0xff) >>> 4
来获得理想的效果。
我猜想b
在移位之前会被扩展到int
。
所以这可能会按预期工作:
(byte)((0x000000FF & b)>>4)
据Bitwise and Bit Shift Operators说:
无符号右移运算符“>>>”将零移动到最左侧位置,而“>>”之后的最左侧位置取决于符号扩展。
因此,使用b >> 4
,你将1111 0001
转换为1111 1111
(b为负,所以它附加1
),这是0xff
。
Java试图通过定义两个不同的移位运算符来避免明确支持无符号基本类型。
该问题讨论了无符号右移,但示例同时执行(有符号和无符号),并显示有符号移位(>>)的值。
您的计算适用于无符号移位(>>>)。
在转换之前,字节操作数被提升为int。
见https://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.19
对每个操作数分别执行一元数字提升(第5.6.1节)。 (不对操作数执行二进制数字提升(第5.6.2节)。)
和https://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html#jls-5.6.1
否则,如果操作数是编译时类型byte,short或char,则通过扩展原语转换(第5.1.2节)将其提升为int类型的值。
字节b =(字节)0xf1;
if(b <0)
d =(字节)((字节)((字节)(b >> 1)&(字节)(0x7F))>>> 3);
其他
d =(字节)(b >>> 4);
首先,检查值:如果值为负。右转,然后是&0x7F,它将变为正。然后你可以轻松地完成其余的右移(4-1 = 3)。
如果值为正,则使用>> 4或>>> 4进行全部右移。它不会对结果产生任何影响,也不会产生任何右移问题。