问题
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 int
s
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