Syntax Highlighter

Wednesday, January 27, 2010

Bitmasking 101

Binary
Binary is read right to left. The rightmost column is bit 0. Then, as you move left, the next is bit 1, 2, ... Numerically, the first column is the "ones" column, the second is the "twos" column, the third is the "fours", the fourth is the "eights" etc. You add the values of the individual columns together for the total number. Here are some examples:

* 00000000 = 0
* 00000001 = 1
* 00000010 = 2
* 00000011 = 3
* 00001001 = 9

The above are 8 bit integers (8 columns) and thus can store a max value of 255 (11111111)

Bitwise Operators
Some of the available bit operators are AND, OR, XOR and NOR.

AND
1 AND 1 = 1
1 AND 0 = 0
0 AND 0 = 0
OR
1 OR 1 = 1
1 OR 0 = 1
0 OR 0 = 0

AND Example
0111 (7)
0101 (5)
--------
0101 (5)

OR Example
0111 (7)
0011 (3)
--------
0111 (7)

Bitwise operations allow you to use one numeric field to represent many values. For example, I like BBQ, so we’ll use a bbq example:
Let’s assume that we are writing software for a restaurant that sells barbecue plates. Each plate can have chicken, sausage, pork ribs, or brisket. You could have these 4 meat choices represented as Boolean values in your BbqPlate class and just set to true if the customer ordered that meat. But, what if you decided to add 10 more meats and 15 vegetables? There must be a better way.
You could use bitwise operations as follows:
DECIMAL BINARY VALUE
1 0001 Chicken
2 0010 Sausage
4 0100 Pork Ribs
8 1000 Brisket

Note that each of the values is represented by a power of two. Now, if you create a plate with sausage, brisket, and pork ribs, you can simply set your unsigned integer by using the bitwise OR operator.
0010 OR 0100 OR 1000 = 1110
The integer value is 14.

Now, when we need to determine the distinct values that have been set, we can use the bitwise AND operator.
1110 AND 0001 = 0000 (therefore, no chicken)
1110 AND 0010 = 0010 (therefore, we have sausage)
1100 AND 0100 = 0100 (therefore, we have pork ribs)
You get the point now…

Another thing you can do if you have a lot of values to represent is use sub-masking. For example, in a 32 bit number, you can use the topmost 4 bits to be a sub-mask. This then allows you to have (4 * 28) distinct values. You could then do the following:

00010000000000000000000000000001
10000000000000000000000000000001

In the bottom 28 bits, these numbers are the same, but the topmost bits make them different.
DECIMAL HEX BINARY
1 0x0001 0000000000000001
2 0x0002 0000000000000010
4 0x0004 0000000000000100
8 0x0008 0000000000001000
16 0x0010 0000000000010000
32 0x0020 0000000000100000
64 0x0040 0000000001000000
128 0x0080 0000000010000000
256 0x0100 0000000100000000
512 0x0200 0000001000000000
1024 0x0400 0000010000000000
2048 0x0800 0000100000000000
4096 0x1000 0001000000000000
8192 0x2000 0010000000000000
16384 0x4000 0100000000000000
32768 0x8000 1000000000000000

Setting the masks
use the bitshift operator to simplify and reduce errors

const int value1 = 1 << 0;
const int value2 = 1 << 1;
const int value3 = 1 << 2;
const int value4 = 1 << 3;


Assigning a value to a variable
use the OR operator

variable |= value1;

Querying to see if a flag is set
use the AND operator

if ((variable & value1) != value1)
OR
if (variable & value1)

Querying for multiple masks

if (variable & (value1 | value2))

Unsetting a flag
use the AND with the inverse of the mask

variable &= ~value1;