gpt4 book ai didi

java - 使用 |= 将字节打包成 long 会产生意想不到的结果

转载 作者:搜寻专家 更新时间:2023-11-01 02:37:42 24 4
gpt4 key购买 nike

我试图将我的 byte[] 数据连接成一个长变量。但是由于某种原因,代码没有按我预期的那样工作。

我有这个字节数组,最大大小为 8 个字节,即 64 位,与 Long 变量的大小相同,因此我试图将此数组连接到 long 变量中。

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);
}




}

结果是:

111111111111111111111111111111111111111111111111101010000000000
111111111111111111111111111111111111111110101000001000100000000
111111111111111111111111111111111111111111111111001001000000000
111111111111111111111111111111111111111100100100101010100000000
111111111111111111111111111111111111111111111111011110000000000
111111111111111111111111111111111111111111111111111100100000000

当最终结果应该是这样的

111111111111111110101000001000110010010010101011011110011111001

即 0xD4,0x11,0x92,0x55,0xBC,0xF9

最佳答案

byte在 Java 中签名,当你这样做时 long |= byte , byte的值被提升并且符号位被扩展,如果byte,这基本上将所有这些高位设置为1是一个负值。

你可以这样做:

 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必须评估。但是, nb是不同的类型。

根据 JLS 15.22.1 :

When both operands of an operator &, ^, or | are of a type that is convertible (§5.1.8) to a primitive integral type, binary numeric promotion is first performed on the operands (§5.6.2).



两个操作数都可以转换为原始整数类型,因此我们引用 5.6.2看看接下来会发生什么。这里的相关规则是:

  1. Widening primitive conversion (§5.1.2) is applied to convert either or both operands as specified by the following rules:

    • ...
    • Otherwise, if either operand is of type long, the other is converted to long.
    • ...


好的,好吧, nlong ,所以根据这个 b现在必须转换为 long使用 5.1.2 中指定的规则.那里的相关规则是:

A widening conversion of a signed integer value to an integral type T simply sign-extends the two's-complement representation of the integer value to fill the wider format.



byte是一个有符号整数值,它被转换为 long ,因此根据此符号位(最高位)简单地向左扩展以填充空间。所以这就是我们的例子中发生的事情(想象一下这里是 64 位,我只是在节省空间):
b = (byte)0xB5                     10110101
b widened to long 111 ... 1111111110110101
n 000 ... 0000000000000000
n | b 111 ... 1111111110110101

等等 n | b计算结果为 0xFFFFFFFFFFFFFFB5 ,不是 0x00000000000000B5 .也就是说,当符号位被扩展并应用 OR 运算时,你就得到了所有这些 1那里基本上覆盖了您之前进行 OR 运算的所有位,然后您的最终结果是不正确的。

都是 byte的结果正在签名和 Java 需要 long | byte要转换为 long | long在执行计算之前。

如果您不清楚这里发生的隐式转换,这里是显式版本:
n = n | (long)b;

解决方法的详细信息

所以现在考虑“解决方法”:
byte b = (byte)0xB5;
long n = 0l;

n |= (b & 255);

所以在这里,我们评估 b & 255第一的。

所以来自 JLS 3.10.1我们看到文字 255类型为 int .

这给我们留下了 byte & int .尽管我们调用了一个与 5.6.2 略有不同的案例,但规则与上面大致相同。 :

Otherwise, both operands are converted to type int.



所以按照这些规则 byte必须转换为 int第一的。所以在这种情况下,我们有:
(byte)0xB5                                10110101
promote to int 11111111111111111111111110110101 (sign extended)
255 00000000000000000000000011111111
& 00000000000000000000000010110101

结果是 int ,它是有符号的,但正如你所看到的,现在它是一个正数,它的符号位是 0。

然后下一步是评估 n | the byte we just converted .所以再次按照上述规则,新的 int加宽为 long , 符号位扩展,但这次:
b & 255                    00000000000000000000000010110101
convert to long 000 ... 0000000000000000000000000010110101
n 000 ... 0000000000000000000000000000000000
n | (b & 255) 000 ... 0000000000000000000000000010110101

现在我们得到了预期的值。

解决方法是转换 bint作为中间步骤并将高 24 位设置为 0,从而让我们将其转换为 long没有原始符号位妨碍。

如果您不清楚这里发生的隐式转换,这里是显式版本:
n = n | (long)((int)b & 255);

其他的东西

并且就像 maraca 在评论中提到的那样,交换循环中的前两行,否则最终会将整个内容向左移动 8 位太远(这就是为什么低 8 位为零的原因)。

我还注意到您预期的最终结果是用领先的 1 填充的。 s。如果这就是你最后想要的,你可以从 -1L 开始而不是 0L (除了其他修复)。

关于java - 使用 |= 将字节打包成 long 会产生意想不到的结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43219560/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com