Why left shifting in java changing the sign value

一笑奈何 提交于 2019-12-10 22:09:04

问题


I am working on java. I am wondering why java producing this output. I am sharing the code here.

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

    }

}

And I got the output as

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

I expected the output should be 0x0000019d


回答1:


You are converting from byte (signed 8 bits) to integer (signed 32 bits). The most significant bit (the leftmost one) holds the sign (see two's complement).

Your 157 is 10011101 in binary. Since you assign this value to a signed byte (java has no unsigned byte), this is in fact a negative number, -99.

Now when you convert from byte to int, the value is preserved. In case of negative numbers, this means setting all the bits to the left to preserve the signedness. In your case, 10011101 becomes 11111111 11111111 11111111 10011101.

Anyway, working with unsigned bytes in java is a nightmare. Basically, you need to mask everything with 0xff (to cut off the 'ones to the left') like this:

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

Beautiful, isn't it?

Update 1: Also, you may be interested in Guava's UnsignedBytes...

Update 2: Java 8 Byte has toUnsignedInt() and toUnsignedLong() methods. Your calculation thus becomes:

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



回答2:


Look. Your expression (arr[1] << 8) | (arr[2] << 16) | (arr[3] << 24) equals to Ox100. So, you need extra Ox9d, which is in decimal is 157. Unfortunatly, byte diapasone is [-128; 127]. So, it is impossible to get 157 from byte. And your a[0] is equal to -99.




回答3:


Because the left most digit is reserved for sign, when you shift left the sign changes when you commit an overflow.




回答4:


In Java, The type byte is a signed value from -128 to 127. This means that (byte)157 result in an overflow and is actually equal to -99. When you output the hexadecimal value of the byte is not apparerent, because the binary representation of -99 (signed) and 157 (unsigned) is equal (and the format conversion %x interprets the value as an unsigned value).

However, as soon as you use it in an expression, the byte value is promoted to an int. And the hexadecimal representation of -99 as an int is 0xffffff9d. You can see this by adding the following line to your program:

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



回答5:


I believe the problem is that bitwise operations in java are performed on ints So when you use

arr[0] | ...

arr[0] is converted to int. So instead of the expected 0x9d, you get 0xffffff9d So the solve the issue you need to & the 0xff mask, e.g.

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


来源:https://stackoverflow.com/questions/35597175/why-left-shifting-in-java-changing-the-sign-value

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!