为什么在java中改变符号值

时间:2016-02-24 08:42:18

标签: java byte-shifting

我正在研究java。 我想知道为什么java产生这个输出。 我在这里分享代码。

public class vvn {

    public static void main(String[] args)
    {
        byte [] arr = new byte[4];
        arr[0] = (byte)157;
        arr[1] = 1;
        arr[2] = 0;
        arr[3] = 0;
        System.out.format("read 0x%x 0x%x 0x%x 0x%x \n",arr[3],arr[2],arr[1],arr[0]);
        int v = (arr[0] | (arr[1] << 8) | (arr[2] << 16) | (arr[3] << 24));
        System.out.format("read 0x%x\n",v);

    }

}

我的输出为

read 0x0 0x0 0x1 0x9d 
read 0xffffff9d

我预计输出应为0x0000019d

5 个答案:

答案 0 :(得分:4)

您正在从字节(带符号的8位)转换为整数(带符号的32位)。最重要的位(最左边的位)保持符号(参见two's complement)。

您的15710011101二进制文件。由于您将此值分配给有符号字节(java没有无符号字节),因此实际上是负数-99

现在,当您从byte转换为int时,将保留该值。如果是负数,这意味着将所有位设置为左侧以保留签名。在您的情况下,10011101变为11111111 11111111 11111111 10011101

无论如何,在java中使用无符号字节是一场噩梦。基本上,你需要用0xff来屏蔽所有内容(以截断'左边的那些'),如下所示:

int v = ((arr[0] & 0xff) |
    ((arr[1] & 0xff) << 8) |
    ((arr[2] & 0xff) << 16) |
    ((arr[3] & 0xff) << 24));

漂亮,不是吗?

更新1:此外,您可能对Guava的UnsignedBytes感兴趣......

更新2: Java 8 Byte具有toUnsignedInt()和toUnsignedLong()方法。因此,您的计算成为:

int v = (Byte.toUnsignedInt(arr[0]) |
    (Byte.toUnsignedInt(arr[1]) << 8) |
    (Byte.toUnsignedInt(arr[2]) << 16) |
    (Byte.toUnsignedInt(arr[3]) << 24));

答案 1 :(得分:1)

看。 你的表达式(arr [1]&lt;&lt; 8)| (arr [2]&lt;&lt; 16)| (arr [3]&lt;&lt; 24)等于Ox100。 所以,你需要额外的Ox9d,十进制是157.不幸的是,字节diapasone是[-128; 127]。所以,从字节中获取157是不可能的。你的a [0]等于-99

答案 2 :(得分:0)

因为最左边的数字是为符号保留的,所以当你向左移动时,符号会在你提交溢出时发生变化。

答案 3 :(得分:0)

在Java中,类型byte是从-128到127的有符号值。这意味着(byte)157导致溢出并且实际上等于-99。当您输出字节的十六进制值时,因为-99(已签名)和157(无符号)的二进制表示相等(并且格式转换%x解释该值作为无符号值)。

但是,只要在表达式中使用它,byte值就会提升为int。并且-99作为int的十六进制表示为0xffffff9d。您可以通过在程序中添加以下行来查看:

System.out.format("0x%x \n", (int)arr[0]);

答案 4 :(得分:0)

我认为问题是java中的按位操作是在int上执行的 所以当你使用

arr[0] | ...

arr[0]已转换为int。因此,不是预期的0x9d,而是0xffffff9d 因此,解决& 0xff掩码所需的问题,例如

int v = ((arr[0] & 0xff) | ((arr[1] & 0xff) << 8) | ...;