我有这段代码
byte[] b = new byte[]{-33,-4,20,30};
System.err.println(Arrays.toString(b));
int x = (b[0] << 24) + (b[1] << 16) + (b[2] << 8) + b[3];
b = new byte[]{(byte)(x >> 24), (byte)(x >> 16), (byte)(x >> 8), (byte)(x)};
System.err.println(Arrays.toString(b));
输出:
[-33, -4, 20, 30]
[-34, -4, 20, 30]
我无法弄清楚为什么这些操作不是反转的。
答案 0 :(得分:6)
您的问题是不需要的符号扩展。
具体而言,b[1]
为-4
,或0xfc
,其符号扩展为0xfffffffc
,然后左移至0xfffc0000
。这具有将最高有效字节递减1的效果。
尝试:
int x = ((b[0] & 0xff) << 24) +
((b[1] & 0xff) << 16) +
((b[2] & 0xff) << 8) +
(b[3] & 0xff);
答案 1 :(得分:3)
请忽略我之前的回答;这是完全错误的。
我认为这里的问题是当你以这种方式编写比特时:
(b[0] << 24) + (b[1] << 16) + (b[2] << 8) + b[3]
你不做你认为你正在做的事情。特别是,假设b[1]
是负数(它是)。当Java进行位移时,它总是在进行转换之前将值提升为int
。这意味着b[1]
在升级后会如下所示:
11111111 11111111 11111111 bbbbbbbb
这里,前导1来自整数的带符号二进制补码表示,这使得负数由许多前导零表示。当你将这个数字向上移动时,你得到了
11111111 bbbbbbbb 00000000 00000000
如果您将这些位添加到(b[0] << 24)
,其格式为
aaaaaaaa 00000000 00000000 00000000
你做不获取
aaaaaaaa bbbbbbbb 00000000 00000000
由于代表中的前导1。要解决此问题,您需要在执行添加之前屏蔽符号位。具体来说,如果你改变了
b[1] << 16
到
(b[1] << 16) & 0x00FFFFFF
然后屏蔽掉要获取的位
00000000 bbbbbbbb 00000000 00000000
所以现在当你添加这两个值时,你得到
aaaaaaaa bbbbbbbb 00000000 00000000
根据需要。
因此,在适当的时间通过在适当的掩码中进行AND运算来形成用于组成比特的正确表达式:
(b[0] << 24) + ((b[1] << 16) & 0x00FFFFFF) + ((b[2] << 8) & 0x0000FFFF) + (b[3] & 0x000000FF)
我在我的系统上测试了它,它似乎工作正常。
希望这有帮助!