Java 使用|=将字节打包为长字符串会产生意外的结果
我试图将byte[]数据连接到一个长变量中。但是由于某些原因,代码并没有像我预期的那样工作 我有一个字节数组,它的最大大小是8个字节,是64位,与长变量的大小相同,所以我尝试将这个数组连接到长变量中Java 使用|=将字节打包为长字符串会产生意外的结果,java,Java,我试图将byte[]数据连接到一个长变量中。但是由于某些原因,代码并没有像我预期的那样工作 我有一个字节数组,它的最大大小是8个字节,是64位,与长变量的大小相同,所以我尝试将这个数组连接到长变量中 public static void main(String[] args) { // TODO Auto-generated method stub byte[] data = new byte[]{ (byte)0xD4,(byte)0x11,(byte
public static void main(String[] args) {
// TODO Auto-generated method stub
byte[] data = new byte[]{
(byte)0xD4,(byte)0x11,(byte)0x92,(byte)0x55,(byte)0xBC,(byte)0xF9
};
Long l = 0l;
for (int i =0; i<6; i++){
l |= data[i];
l <<=8;
String lon = String.format("%064d", new BigInteger(Long.toBinaryString((long)l)));
System.out.println(lon);
}
}
publicstaticvoidmain(字符串[]args){
//TODO自动生成的方法存根
字节[]数据=新字节[]{
(字节)0xD4,(字节)0x11,(字节)0x92,(字节)0x55,(字节)0xBC,(字节)0xF9
};
长l=0l;
对于Java中的(int i=0;ibyte
是有符号的,当您执行long |=byte
时,byte
的值被提升,符号位被扩展,如果byte
是负值,则基本上将所有较高的位设置为1
您可以这样做:
l |= (data[i] & 255)
将其强制为int
并在升级为long
之前终止该符号
细节
先决条件:如果“符号位”一词对您没有意义,那么您必须先阅读。我不会在这里解释它
考虑:
byte b = (byte)0xB5;
long n = 0l;
n |= b; // analogous to your l |= data[i]
请注意,n |=b
完全等同于n=n | b
(),因此我们将研究它
因此首先必须计算n | b
,但是,n
和b
是不同的类型
根据:
当运算符&、^或|的两个操作数都是可转换为基元整数类型(§5.1.8)的类型时,首先对操作数(§5.6.2)执行二进制数字提升
这两个操作数都可转换为基元整数类型,因此我们将参考以了解接下来会发生什么。此处的相关规则如下:
加宽基元转换(§5.1.2)用于转换以下规则指定的一个或两个操作数:
- 否则,如果任一操作数的类型为
long
,则另一个操作数将转换为long
好的,n
是long
,因此根据此b
现在必须使用中指定的规则转换为long
。相关规则有:
将带符号整数值扩大转换为整数类型T simply sign扩展了整数值的两个补码表示形式,以填充更广泛的格式
byte
是一个有符号整数值,它被转换成一个long
,因此根据这一点,符号位(最高位)被简单地扩展到左侧以填充空间。这就是我们的示例中所发生的情况(想象一下这里的64位我只是在节省空间):
因此,n | b
的计算结果是0xffffffffffb5
,而不是0x00000000000000B5
。也就是说,当符号位被扩展并且OR操作被应用时,所有这些1
基本上覆盖了之前输入的字节中的所有位,最终结果是incorrect
这都是byte
被签名的结果,Java需要在执行计算之前将long | byte
转换为long | long
如果您不清楚此处发生的隐式转换,以下是显式转换:
n = n | (long)b;
n = n | (long)((int)b & 255);
解决方法的详细信息
现在考虑“解决办法”:
因此,我们首先评估b&255
因此,我们可以看到,文本255
的类型是int
这就给我们留下了byte&int
。尽管我们调用的案例与上面的略有不同,但规则大致相同:
否则,两个操作数都将转换为类型int
因此,根据这些规则,必须首先将byte
转换为int
。因此在这种情况下,我们有:
(byte)0xB5 10110101
promote to int 11111111111111111111111110110101 (sign extended)
255 00000000000000000000000011111111
& 00000000000000000000000010110101
结果是一个int
,它是有符号的,但是正如你所看到的,现在它是一个正数,它的符号位是0
然后下一步是计算刚才转换的字节n
。因此,根据上述规则,新的int
被加宽为long
,符号位扩展,但这次:
b & 255 00000000000000000000000010110101
convert to long 000 ... 0000000000000000000000000010110101
n 000 ... 0000000000000000000000000000000000
n | (b & 255) 000 ... 0000000000000000000000000010110101
现在我们得到了预期的价值
解决方法是将b
转换为int
作为中间步骤,并将高24位设置为0,这样我们就可以将其转换为long
,而不必使用原始符号位。
如果您不清楚此处发生的隐式转换,以下是显式转换:
n = n | (long)b;
n = n | (long)((int)b & 255);
其他东西
正如maraca在评论中提到的,交换循环中的前两行,否则最终会将整个循环的8位向左移动太远(这就是为什么低8位为零)
我还注意到,您预期的最终结果用前导的1
s填充。如果这是您想要的结果,您可以从-1L
开始,而不是0L
(除了其他修复).交换for循环中的前两行,没有理由在这里使用Long intead of Long。有趣的回答谢谢,它是有效的。但是我不明白你对值提升和符号位扩展的意思是什么?谢谢!@JasonC很好的解释!
b & 255 00000000000000000000000010110101
convert to long 000 ... 0000000000000000000000000010110101
n 000 ... 0000000000000000000000000000000000
n | (b & 255) 000 ... 0000000000000000000000000010110101
n = n | (long)((int)b & 255);