我试图将byte []数据连接成一个long变量。但由于某种原因,代码没有像我预期的那样工作。
我有这个字节数组,最大大小为8个字节,为64位,长度变量大小相同,所以我试图将这个数组连接成长变量。
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);
}
}
结果是:
1111111111111111111111111111111111111111111111111101010000000000 1111111111111111111111111111111111111111110101000001000100000000 1111111111111111111111111111111111111111111111111001001000000000 1111111111111111111111111111111111111111100100100101010100000000 1111111111111111111111111111111111111111111111111011110000000000 1111111111111111111111111111111111111111111111111111100100000000
最终结果应该是
111111111111111110101000001000110010010010101011011110011111001
,即0xD4,0x11,0x92,0x55,0xBC,0xF9
答案 0 :(得分:4)
byte
已签名,当您执行long |= byte
时,byte
的值被提升并且符号位被扩展,这实际上将所有这些高位设置为1如果byte
是负值。
您可以这样做:
l |= (data[i] & 255)
要强制它进入int
并在它之前杀死该符号,然后将其提升为long
。 Here is an example of this happening
先决条件:如果术语“符号位”对您没有意义,那么您必须先阅读What is “2's Complement”?。我不会在这里解释。
考虑:
byte b = (byte)0xB5;
long n = 0l;
n |= b; // analogous to your l |= data[i]
请注意,n |= b
完全等同于n = n | b
(JLS 15.26.2),所以我们会看一下。
因此必须首先评估n | b
。但是,n
和b
是不同的类型。
根据JLS 15.22.1:
当运算符&amp;,^或|的两个操作数时是一种可转换(第5.1.8节)到基本整数类型的类型,首先对操作数执行二进制数字提升(第5.6.2节)。
两个操作数都可以转换为原始整数类型,因此我们咨询5.6.2以了解接下来会发生什么。这里的相关规则是:
- 醇>
应用扩展基元转换(第5.1.2节)来转换由以下规则指定的一个或两个操作数:
- ...
- 否则,如果任一操作数的类型为
long
,则另一个操作数将转换为long
。- ...
好的,n
为long
,因此根据此b
,必须使用5.1.2中指定的规则将long
转换为byte
。相关规则是:
将有符号整数值的扩展转换为整数类型T只需对整数值的二进制补码表示进行符号扩展,以填充更宽的格式。
好long
是一个带符号的整数值,它被转换为b = (byte)0xB5 10110101
b widened to long 111 ... 1111111110110101
n 000 ... 0000000000000000
n | b 111 ... 1111111110110101
,因此符号位(最高位)只是向左扩展以填充空格。所以这就是我们的例子中发生的事情(想象64位,我只是在节省空间):
n | b
因此0xFFFFFFFFFFFFFFB5
评估为0x00000000000000B5
,不评估 1
。也就是说,当该符号位被扩展并且应用了OR操作时,你所有那些byte
基本上覆盖了你已经存在的前一个字节中的所有位,并且那么,最终结果是不正确的。
这是long | byte
被签名和Java需要long | long
在执行计算之前转换为n = n | (long)b;
的所有结果。
如果你不清楚这里发生的隐式转换,这里是显式版本:
byte b = (byte)0xB5;
long n = 0l;
n |= (b & 255);
所以现在考虑“解决方法”:
b & 255
所以在这里,我们首先评估255
。
因此,从JLS 3.10.1我们可以看到文字int
的类型为byte & int
。
这给我们留下了int
。虽然我们引用了与5.6.2:
否则,两个操作数都将转换为
byte
类型。
因此,根据这些规则int
必须首先转换为(byte)0xB5 10110101
promote to int 11111111111111111111111110110101 (sign extended)
255 00000000000000000000000011111111
& 00000000000000000000000010110101
。所以在这种情况下我们有:
int
结果是n | the byte we just converted
,已签名,但正如您所见,现在是一个正数,其符号位为0 。
然后下一步是评估int
。因此,根据上述规则,新long
被扩展为b & 255 00000000000000000000000010110101
convert to long 000 ... 0000000000000000000000000010110101
n 000 ... 0000000000000000000000000000000000
n | (b & 255) 000 ... 0000000000000000000000000010110101
,符号位扩展,但这一次:
b
现在我们得到了预期的价值。
该解决方法的工作原理是将int
转换为long
作为中间步骤并将高24位设置为0,从而让我们将 转换为n = n | (long)((int)b & 255);
没有原始标志位妨碍。
如果你不清楚这里发生的隐式转换,这里是显式版本:
1
也像maraca在评论中提到的那样,在你的循环中交换前两行,否则你最终会将整个事物向左移动到最左边8位(这就是你的低8位为零的原因)。 / p>
另外,我注意到您的预期最终结果是使用前导-1L
填充的。如果这就是你想要的结果,你可以从0L
而不是{{1}}开始(除了其他修正)。