Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Byte shift inverse operation

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.

like image 527
PeterMmm Avatar asked Jan 19 '23 04:01

PeterMmm


2 Answers

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);
like image 65
finnw Avatar answered Jan 24 '23 17:01

finnw


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!

like image 23
templatetypedef Avatar answered Jan 24 '23 15:01

templatetypedef