To Bitwise is Geeky!

I've learnt from experience that people tend to get repelled from programs using bitwise operators especially the bit shifts. I, however, find a program which uses bitwise operators neater and cool.

A bit of the background before we begin. I assume that you know a bit about Number Systems (Binary and Hexadecimal). I use Java syntax in this article just to clear the concept of signed and unsigned right shift but the concepts in general are applicable to any programming language.

Bitwise Operators

AND

0 & 0 = 0
0 & 1 = 0
1 & 0 = 0
1 & 1 = 1

The result is 1 when both operands are 1, 0 otherwise.

OR

0 | 0 = 0
0 | 1 = 1
1 | 0 = 1
1 | 1 = 1

The result is 1 when either of the operands is 1.

NOT

!0 = 1
!1 = 0

The result is the inverse of input i.e. 0 becomes 1 and vice versa.

XOR

0 ^ 0 = 0
0 ^ 1 = 1
1 ^ 0 = 1
1 ^ 1 = 0

The result is addition modulo 2 i.e. 1 when exactly one of the operands is 1.

Bit Shifts

For further examples let us take a variable x:

int x=5;

As we know an integer takes 32 bit memory so x in binary is:

binary(x) = 00000000000000000000000000000101

Left Shift <<

It shifts the bits of the number to left the specified amount of times and zeros are padded to the right.

For instance let us try shifting 2 bits of x to left.

x = x<<2;
binary(x) = 00000000000000000000000000010100
decimal(x) = 20

We see that on shifting left by each bit the number is doubled and because we shifted x by 2 bits it has quadrupled i.e. 5*4 = 20.

Unsigned Right Shift >>>

It shifts the bits of the number to right the specified amount of times and zeros are padded to the left.

For instance let us try shifting 3 bits of x to right.

x = 64;
binary(x) = 00000000000000000000000001000000
x = x>>>3;
binary(x) = 00000000000000000000000000001000
decimal(x) = 8

As we see the bits have been shifted 3 places to the right and zeros are padded to the beginning of the binary string. The bits that overflow to the right are discarded and lost and cannot be recovered by a left shift. Each right shift halves the number so a shift of 3 bit will convert it to one-eighth i.e. 64/8 = 8.

The name of this shift operator is unsigned because it does not depend on the sign of the number. We'll see what that means in the next shift operator.

Signed Right Shift >>

It shifts the bits of the number to right the specified amount of times and pads zeroes or ones to the left if the number is positive or negative respectively.

int x = 64;
binary(x) = 00000000000000000000000001000000
x = x>>3;
binary(x) = 00000000000000000000000000001000
decimal(x) = 8
int y = -73;
binary(y) = 11111111111111111111111110110111
y = y>>3;
binary(y) = 11111111111111111111111111110110
decimal(y) = -10

As we see the that a signed shift on a positive number is exactly the same as an unsigned shift but in case of a negative number the left is padded with 1s instead of 0s.

Implementation

You may have seen color codes written in hexa-decimal like 0xFFAB274F or #AB274F which by the way is the hex of Amarnath Purple.

Let us extract the red, green, blue and alpha values from this hex using our knowledge of bitwise operators.

int purple = 0xffab274f;
binary(purple) = 11111111101010110010011101001111

Now, we know that the first 8 bits are the alpha (ff), next 8 bits are the red (ab), next 8 bits are the green (27) and the last 8 bits are blue (4f).

We know that binary(0xff) = 11111111 = 255.

Now, if we shift 24 bits to the right (unsigned) then we are left with the first 8 bits which is the alpha value.

Then, if we shift 16 bits to the right (unsigned) then we are left with the first 16 bits which is the alpha value + the red component of the color. So, how do we extract the red component? Simply & the value with 0xff.

int purple = 0xffab274f;
int alpha = purple>>>24;
binary(alpha) = 11111111
int alpha_and_red = purple>>>16;
binary(alpha_and_red) = 1111111110101011

Now, let see how to get red from alpha_and_red. We & it with 0xff which is 0000000011111111 and because the & of anything with 0 is zero we get only the red bits as a result. Here's how:

int red = alpha_and_red & 0xff;
  1111111110101011
& 0000000011111111
  ----------------
  0000000010101011

We can see we only get red bits as output and others are discarded. We can do similar things for blue and green.

Here's the code snippet in java:

int color = 0xffab274f;
int alpha = color >>> 24;
int red = (color >>> 16)&0xff;
int green = (color >>> 8)&0xff;
int blue = (color)&0xff;