Why left shifting in java changing the sign value

3

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

java
byte-shifting
asked on Stack Overflow Feb 24, 2016 by AQU

5 Answers

5

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));
answered on Stack Overflow Feb 24, 2016 by prook • edited Feb 24, 2016 by prook
1

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.

answered on Stack Overflow Feb 24, 2016 by Solorad
0

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

answered on Stack Overflow Feb 24, 2016 by Rupesh • edited Feb 24, 2016 by Rupesh
0

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]);
answered on Stack Overflow Feb 24, 2016 by Hoopje
0

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) | ...;
answered on Stack Overflow Feb 24, 2016 by radoh

User contributions licensed under CC BY-SA 3.0