Friday, 10 May 2013

Bitwise Operations in Java

The bitwise operations in Java are rarely discussed, and you can sense that by reading the official tutorials on this topic. However, there are interesting usages that you should probably be aware of.

The bitwise operators operates on integral types (int and long). Before explaining how bitwise operators work, you should have an idea of how integers are represented in the binary form. As mentioned by the official primitive data type tutorials, the int data type is a 32-bit signed 2's complement integer, whose value ranges from -2^32 to 2^32-1, inclusively. On the other hand, the long data type is a 64-bit signed 2's complement integer, whose value ranges from -2^64 to 2^64-1, inclusively. In both cases, the highest order bit of int and long shows the sign of the value and is not part of the numeric value, where 0 being positive and 1 being negative.

For example, an int value of 1 in binary form looks like this
00000000000000000000000000000001
while an int value of -1 in binary form looks like this
11111111111111111111111111111111
The negative value is transformed in 2 steps:
Step 1: bitwise complement of +1, such that
00000000000000000000000000000001 
becomes
11111111111111111111111111111110

Step 2: add 1, such that
11111111111111111111111111111110 
becomes
11111111111111111111111111111111
Given a negative value, where the highest (leftmost) bit is 1, you can work out what value the binary form represents in 2 steps:
Step 1: bitwise complement of the binary value, such that
11111111111111111111111111111011
becomes
00000000000000000000000000000100

Step 2: add 1, such that
00000000000000000000000000000100
becomes
00000000000000000000000000000101
Therefore, the original binary form represents -5.

The bitwise operators

OperatorNameExampleResultDescription
a & band6 & 901 if both bits are 1, or 0 if either bit is 0
a | bor 6 | 9151 if either bit is 1, or 0 if both bits are 0
a ^ bxor 6 ^ 9151 if both bits are different, 0 if both bits are the same
~anot ~9 -10Inverts the bits. (equivalent to +1 and then change sign)
n << pleft shift60 << 2240Shifts the bits of n left p positions. Zero bits are shifted into the low-order positions.
n >> pright shift60 >> 215Shifts the bits of n right p positions. If n is a 2's complement signed number, the sign bit is shifted into the high-order positions.
n >>> punsigned right shift-60 >>> 204095Shifts the bits of n right p positions. Zeros are shifted into the high-order positions.

Packing and Unpacking

Multiple int values of limited range can be "packed" into one int, by using the bitwise operators. For example, we have the following integer variables age (range 0~255, or 8-bit), gender (0~1, or 1-bit), weight (range 0~255, or 8-bit), and height (range 0-255, or 8-bit). These integers can be packed and unpacked into/from a 32-bit integer like this:

Represent boolean values in a byte

Suppose we have 8 boolean values that are true or false, and we want to store them in a single byte, and sent it across the network, and then unpack it and use it to toggle 8 different things:


Multiply and Divide

When using x << 3, it is equivalent to multiply x by 8 (or 2^3); similarly, when using x >> 2, it is equivalent to divide x by 4 (or 2^2). In fact, a smart enough compiler will replace your multiplication or division expressions using bit shift operators such that it is more compact and faster. So there is no need to explicitly use bit shift operator if you want multiply or divide by a power of 2!