Java: Bitshifting bytes

Written on April 29, 2015

TL;DR When bitshifting a byte use a & 0xFF mask

Working at the bit level in Java can be very frustrating. This is for two reasons

  1. There are no unsigned number primitives
  2. Automatic type promotion is not intuitive!

Looking at type promotion, whenever you use a bitwise operator on a byte variable it is automatically promoted to an int.

This means you have to write casting code such as

byte b = (byte) (b ^ 8); or b ^= 8 which does the former under-the-hood.

This is not too bad really. But the pain starts when working with signed numbers together with int promotion. For example

ex1.java

For bByte we would expect the result to be 0b1111_1001 as the right Arithmetic shift operator >> fills the left bit depending on the left most (sign) bit (which is 1 when negative ala 2’s complement) so the result is as expected.

However for cByte we would expect the result to be 0b0000_1001 as the right Logical shift operator >>> should fill the left most bit with 0, but we still get -7! Why is this happening?

Well due to the auto int promotion (as a result of using any bitwise operator), when the aByte is promoted to an int, the left most bits of that int are all 1s. In binary form this is

0b1111_1111_1111_1111_1111_1111_1001_0000 before the shift

0b0000_1111_1111_1111_1111_1111_1111_1001 after the shift

Due to how casting works when casting down the last 8-bits will be copied directly, hence still giving 0b1111_1001. Not very intuitive when you see aByte >>> 4 imho.

So how can we perform >>> operations and get the “intuitive” result of 0b0000_1001?

Note: This is useful as to be able to do as Java does not have an unsigned byte type the above kind of operation would let you use a java byte in place of another languages unsigned byte type. This can useful when porting code, for example, if you want to replicate a ubyte >> 4 call then the below will be useful.

The answer is to mask the bitwise result before casting back down to byte. We can do this with & 0xFF. This works by persevering only the last 8 bits of the promotioned-to int only, and dropping all the extra 1 bits.When casting down we then get the result we are looking for. I.e.

ex2.java

This is a bit clunky but a valuable technique if you are encountering unexpected results from your bitshifts!