字节移位逆操作

时间:2011-11-30 19:28:49

标签: java byte bit-manipulation bitwise-operators

我有这段代码

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]

我无法弄清楚为什么这些操作不是反转的。

2 个答案:

答案 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)

我在我的系统上测试了它,它似乎工作正常。

希望这有帮助!