Click here to Skip to main content
Email Password   helpLost your password?

Background

After reading the wonderful article, “An introduction to bitwise operators” by PJ Arends, I thought of extending the scenario further and thought of putting few common uses of bitwise operations that I have faced in real life. The above mentioned article is indeed a pre-requisite to this one.

Bitwise operations are really a joy to use. If used with care, they gives a tremendous good look to the code and performance too.

Using the Code

So, let’s start with a vintage problem introduced by Denis Ritchie, “You have a variable (say int) at your disposal, you need to find out how many bits are set (i.e. 1) in the bit pattern beneath”.

Say, you have declared an int var = 13, then the bitwise structure of var will be:

512

256

128

64

32

16

8

4

2

1

0

0

0

0

0

0

1

1

0

1

Shaded cells indicate the power of each bucket, in the higher order, all buckets actually exist, but we ignored them for this example. Now let’s write a method that will return the number of bit sets in this variable:

int __numOf_SET_Bits(int var){
 if (var==0) return 0;
 else return (var&01)?1+__numOf_SET_Bits(var>>1):__numOf_SET_Bits(var>>1);
}       

A simple recursive function that counts the total number of set bits in the bit pattern for a variable. In the heart of the function, there exist two bitwise operations:

  1. One which checks whether the current right most bit has a numeric value of 1 or not (checks by a bitwise AND with a 1), if so increment the total count by one.
  2. While calling the function recursively, make a right shift by 1 over the variable, thus truncating the right most bit of the variable (and left most bit will be padded by a zero).

There also exists an iterative version of this algorithm, please refer “C Programming Language” book by Denis Ritchie.

Ok, now let’s analyze another similar kind of problem, “Find out whether a number is even or odd”. The naïve algorithm is:

#define isEven(a) \
  ((((a)%2)==0)?1:0)

But if you closely look at the bit pattern for any variable, you can see all the buckets in the bit pattern have a power of even number except the right most one (which has a power of 1). Now since (Even Number + Even Number) = Even Number and (Even Number + Odd Number) = Odd Number, we can conclude for a even number the right most bit must have a value ZERO, and for an odd number the right most bit must be set to ONE. So let’s try to write a function/macro considering this property:

#include <stdio.h>
#define isEven(a) \
  ((((a)&01)==0)?1:0)
int main()
{
 int var=1;
 if(isEven(var)){
    printf("%d is a even number \n",var);
 }
 else
    printf("%d is a odd number \n",var);
 return 0;
}

Wow, this one looks cool.

Now let's analyze another problem, “Find out whether a number has a form of power of 2 or not”, i.e. all the numbers 1,2,4,8,16,32,64.128……. have a form of power of 2. How to find this out? I am going to give two solutions.

If you look closely at the bit pattern, all the numbers have a form of power of 2, for all of them exactly one bit is set, rest all are zero in the bit pattern. Now if we bitwise-AND-ed that number with a number having a value less than 1, the operational outcomes will be zero. Consider the example of 128:

Number

128

64

32

16

8

4

2

1

128

1

0

0

0

0

0

0

0

128-1=127

0

1

1

1

1

1

1

1

128 bitwise-AND 127

0

0

0

0

0

0

0

0

Voila, we can use this property indeed. Let's write our first version of the code:

#include <stdio.h>
#define __isPower_of_TWO(a) \
 (((a)&(a-1))==0)?1:0
int main(){
 int arr[] = {1,2,3,4,5,8,9,16};
 int i=0;
 for(;i<sizeof(arr)/sizeof(arr[0]);i++){
        if (__isPower_of_TWO(*(arr+i)))
           printf("%d has a form of Power of Two \n",*(arr+i));
        else
           printf("%d is not in the form \n", *(arr+i));
 }
 return 0;
}

Now consider another way to do this:

#include <stdio.h>
#define __isPower_of_TWO(a) \
 (((a)&(-a))==a)?1:0
int main(){
 int arr[] = {1,2,3,4,5,8,9,16};
 int i=0;
 for(;i<sizeof(arr)/sizeof(arr[0]);i++){
        if (__isPower_of_TWO(*(arr+i)))
           printf("%d has a form of Power of Two \n",*(arr+i));
        else
           printf("%d is not in the form \n", *(arr+i));
 }
 return 0;
}

Really it’s another nice way to do the same job.

Now consider another famous problem of swapping the value of two variables without using the third one. The naïve algorithm uses the arithmetic operator to do so. The smarter way to do it is to use Bitwise-XOR operation, the XOR operation has a unique property: returns 0 when the bits have the same value (both zero, or both one) and returns 1 when their values are different (one is zero and the other is one). Let’s first write the logic, and then we will see it with an example.

#include <stdio.h>
void __SWAP(int *a,int *b){
 *a = *a ^ *b;
 *b = *a ^ *b;
 *a = *a ^ *b;
}
int main()
{
 int a=5, b=6;
 printf("Before swap: a=%d <=====> b=%d \n",a,b);
 __SWAP(&a,&b);
 printf("After  swap: a=%d <=====> b=%d \n",a,b);
 return 0;
}

In the core of the logic, there are three sequential bitwise-XOR operations, which swap the value in-turn. Awesome, let's see an example.

Initially bit-pattern of a and b is:

a

0

0

0

0

0

1

0

1

b

0

0

0

0

0

1

1

0

Now after first bitwise-XOR operation, value of “a” and “b” become: (check only the bit-pattern of “a” will change, “b” will remain the same for this step. (* indicates which bit has changed the state).

a

0

0

0

0

0

0 (*)

1 (*)

1

b

0

0

0

0

0

1

1

0

After the second operation: (here only “b” will be changed, and check bit pattern of “b” now contains original bit-pattern of a, so the value of “a” has been shifted over b).

a

0

0

0

0

0

0

1

1

b

0

0

0

0

0

1

0 (*)

1 (*)

After the third operation: (here only value of “a” will be changed, and voila! Look the value of “b” has been shifted over “a”, swap operation got passed).

a

0

0

0

0

0

1 (*)

1

0 (*)

b

0

0

0

0

0

1

0

1

Now let’s analyze a data structure building technique, named “XOR linked list”. A doubly linked list is a list for which each node contains a data member and two pointers pointing to the previous and next item in the list. For the last node in the list the value of the “next” is NULL, for the first node (head) the value of “prev” is NULL. If a head itself contains a NULL then the list is empty. Since each node contains two address parts (one for predecessor and another for successor) the storage requirement gets higher. A bitwise-XOR operation can compress the same information into one address field.

Consider the following code:

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
typedef struct XOR_based_Node {
 int data;
 unsigned long compressedAddress;
}node;
node* head = NULL;
void add_element_to_list(node** headRef, int data)
{
  node *newNode = (node*)malloc(sizeof(node));
 assert(newNode);
 newNode->compressedAddress = (unsigned long)(*headRef);
 newNode->data = data;
 if(*headRef != NULL)
  (*headRef)->compressedAddress ^= (unsigned long)newNode;
 *headRef=newNode;
}
void printList(node* head)
{
  unsigned long prev = 0;
 while(head) {
  unsigned long next = prev ^ head->compressedAddress;
  printf("%d ", head->data);
  prev = (unsigned long)head;
  head = (node *)next;
 }
 printf("\n");
}
int main(void) {
 int i=0;
 for(;i<10;i++)
   add_element_to_list(&head,i);
 printList(head);
 return 0;
}

In this code, all the address calculations have been done in a single address field (that we have per node). This field has been calculated while we try to push a node in the list using bitwise-XOR operation and the proper memory location has been fetched using bitwise-XOR operation too.

Given below is the output:

bash-3.2$ gcc -g -o hello XoR_List.c 
bash-3.2$ ./hello
9 8 7 6 5 4 3 2 1 0 
bash-3.2$

Conclusion

There are tons and tons of real life examples of using bitwise operations. In the second part of the article, I'll focus on the data structure only (heap, priority queue, red-black tree, splay tree) and will try to explain how a bitwise operation can help us greatly to implement those.

References

  1. "C Programming Language" by Denis Ritchie

History

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
GeneralIsEven.. fora double
CanopenR
11:16 30 Mar '09  
Is there a isEven for a double?
Generalswap [modified]
J0ker
18:39 12 Sep '08  
it looks better:
*a ^= *b ^= *a ^= *b;
but consider traditional swap with a temporary variable is faster...

UPD it's undefined behavior

modified on Wednesday, September 24, 2008 11:07 AM

GeneralRe: swap
programmersmind
6:57 15 Sep '08  
Agreed sir, this is an "Awesome One-liner", thanks for suggesting.

T.S Chowdhury

GeneralRe: swap
Bartosz Wojcik
9:19 23 Sep '08  
__asm mov eax,var
__asm bswap eax
__asm mov var,eax

but unfortunetely it's for 586 and we are all stuck in 386 compatible mode everywhere...


RantRe: swap - undefined behavior
J0ker
6:07 24 Sep '08  
sorry for destruction, but:
*a ^= *b ^= *a ^= *b;
is undefined behavior
so don't use it
good one-expression replacement is:
*a ^= *b, *b ^= *a, *a ^= *b;
GeneralBeware of signed numbers
billyvas
20:38 25 Aug '08  
signed and unsigned version of a number doesn't contain same number of bits, depending on internal representation. So if you don't want to count the sign bit, you have to mask it first.
GeneralRe: Beware of signed numbers
programmersmind
12:28 3 Sep '08  
agreed, will take care of tht in future.

Thanks for the catch

T.S Chowdhury

GeneralOther Operations
Hawk777
15:47 25 Aug '08  
There are other, more advanced things that can be done with bitwise operations than are listed here.

For one thing, the algorithm to count set bits presented in this article is not particularly efficient. A more efficient solution is this:


unsigned int count_bits(unsigned int value) {
value = (value & 0x55555555U) + ((value >> 1 ) & 0x55555555U);
value = (value & 0x33333333U) + ((value >> 2 ) & 0x33333333U);
value = (value & 0x0F0F0F0FU) + ((value >> 4 ) & 0x0F0F0F0FU);
value = (value & 0x00FF00FFU) + ((value >> 8 ) & 0x00FF00FFU);
value = (value & 0x0000FFFFU) + ((value >> 16) & 0x0000FFFFU);
return value;
}


which takes only ten ANDs, five shifts, and five adds for a total of twenty operations, whereas the provided example goes to a recursive depth of 32 and does one AND, one (potentially expensive!) conditional, one shift, and (maybe) one add for each level, for a total of between 96 and 128 operations.

The algorithm above works by dealing with multiple blocks of bits in parallel. Rather than considering "value" as just a pile of bits, consider it to be a list of buckets each of which contains a counter indicating the number of bits set within a particular subrange of the original value. Initially each bit is its own bucket: given a single bit, obviously, if the bit is zero, then zero bits are set within that range, and if the bit is one, then one bit is set within that range. Each line of the above algorithm takes every pair of adjacent buckets and replaces them with a single larger bucket containing the sum of the original two buckets. For example, the first line takes the original value (with 32 1-bit buckets) and takes every adjacent pair of bits, adds them together (thus giving a value 0, 1, or 2), and stores the result into a 2-bit-long bucket in the space where the original two bits were. The second line similarly turns the 16 2-bit buckets into 8 4-bit buckets, and so on. The last line ends up giving you a single 32-bit bucket, which is the number of bits set in the original value.



Another interesting thing you can do with bitwise operations is set-related stuff. Assuming you represent a set as an integer (where a 1 bit means the corresponding element is present and a 0 bit means it's absent), you can take the union of two sets with OR and the intersection with AND. You can also find all possible subsets of a set like so:


for (unsigned int subset = set; subset; subset = (subset - 1) & set) {
// Use this subset.
}


noting that this will *include* the original set but *exclude* the empty set. The key to understanding this algorithm is to realize that "subset - 1", due to the way borrowing works in binary subtraction, will *remove* the smallest element that's present, while *inserting* every element below it. ANDing with "set" then limits "every element below it" to be only those present in the original set.
GeneralRe: Other Operations
programmersmind
16:10 25 Aug '08  
I indeed agreed...........the reason I avoid the logic for counting the no of bits (given by you) is for simplicity. The similar logic used by any modern compiler/engine (like Java), and its weird to read and/or understand.

Though I appreciate your comments from the core of my hearts. Thanks for the catch.

T.S Chowdhury

GeneralRe: Other Operations
wtwhite
16:57 25 Aug '08  
Another way to count 1 bits is to repeatedly eliminate the lowest 1 bit until the result is 0. The lowest 1 bit can be found using the (x&(x-1)) trick programmersmind described for detecting a power of 2. This approach is faster than programmersmind's original technique of looping through all bits, but less efficient than hawk777's cute technique (unless you are expecting a small number of 1 bits).

One more thing: I'm intrigued to know why you sometimes refer to array elements using the usual arr[i] syntax and sometimes use *(arr+i)...

WTJW
GeneralRe: Other Operations
Hawk777
21:15 25 Aug '08  
Quite understandable, of course Smile

It's quite amazing how many ways people have come up with to count set bits in a word.
General"Find out whether a number has a form of power of 2 or not"
phinecos
17:03 21 Aug '08  
just use your above method,if your find out the number of bits are set (1) in the bit pattern beneath equals to 1,it must be form of power of 2
code as follow:


int __numOf_SET_Bits(int var)
{
if (var==0) return 0;
else return (var&01)?1+__numOf_SET_Bits(var>>1):__numOf_SET_Bits(var>>1);
}

int main()
{
int arr[] = {1,2,3,4,5,8,9,16};
int i=0;
for(;i {
if (__numOf_SET_Bits(arr[i])==1)
printf("%d has a form of Power of Two \n",*(arr+i));
else
printf("%d is not in the form \n", *(arr+i));
}
return 0;
}
GeneralRe: "Find out whether a number has a form of power of 2 or not"
peterchen
12:25 25 Oct '08  
This can be tested even easier:

bool IsPowerOfTwo(int x)
{
return x & (x-1) == 0;
}

this has one edge case of 0 being identified as power of two, if 0 is a possible value this needs to be tested separately.


GeneralComment
pwasser
15:11 19 Aug '08  
Can not see how the content of this article matches the title in any way. Demystified is a strong word that raises strong expectations - in this case not met.

Peter Wasser

GeneralRe: Comment
programmersmind
15:15 19 Aug '08  
Hope the changed name will help. Thanks for catching.

T.S Chowdhury


Last Updated 19 Aug 2008 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010