I have this code
byte[] b = new byte[]{-33,-4,20,30};
System.err.println(Arrays.toString(b));
int x = (b[0] << 24) + (b[1] << 16) + (b[2] << 8) + b[3];
b = new byte[]{(byte)(x >> 24), (byte)(x >> 16), (byte)(x >> 8), (byte)(x)};
System.err.println(Arrays.toString(b));
Output:
[-33, -4, 20, 30]
[-34, -4, 20, 30]
I cannot figure out why this operations are not invers.
Your problem is unwanted sign extension.
Specifically, b[1]
is -4
, or 0xfc
which is sign-extended to 0xfffffffc
then left-shifted to 0xfffc0000
. This has the effect of decrementing the most-significant byte by 1.
Try:
int x = ((b[0] & 0xff) << 24) +
((b[1] & 0xff) << 16) +
((b[2] & 0xff) << 8) +
(b[3] & 0xff);
Please disregard my previous answer; it's totally wrong.
I think that the problem here is that when you compose the bits in this manner:
(b[0] << 24) + (b[1] << 16) + (b[2] << 8) + b[3]
You are not doing what you think you're doing. In particular, suppose that b[1]
is negative (which it is). When Java does bitshifts, it always promotes the value to an int
before doing the shift. This means that b[1]
will look like this when it gets promoted:
11111111 11111111 11111111 bbbbbbbb
Here, the leading 1s are from the signed two's-complement representation of integers, which makes negative numbers represented by a lot of leading zeros. When you shift this number up, you get
11111111 bbbbbbbb 00000000 00000000
If you then add these bits to (b[0] << 24)
, which has the form
aaaaaaaa 00000000 00000000 00000000
You do not get
aaaaaaaa bbbbbbbb 00000000 00000000
Because of the leading 1s in the representation. To fix this, you need to mask out the sign bits before doing the addition. Specifically, if you change
b[1] << 16
to
(b[1] << 16) & 0x00FFFFFF
Then you mask out the bits to get
00000000 bbbbbbbb 00000000 00000000
So now when you add the two values, you get
aaaaaaaa bbbbbbbb 00000000 00000000
As desired.
The correct expression for composing bits is thus formed by ANDing in the appropriate masks at the appropriate times:
(b[0] << 24) + ((b[1] << 16) & 0x00FFFFFF) + ((b[2] << 8) & 0x0000FFFF) + (b[3] & 0x000000FF)
I've tested this on my system and it seems to work just fine.
Hope this helps!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With