Click here to Skip to main content
Click here to Skip to main content

An introduction to bitwise operators

By , 8 May 2002
 

Introduction

I have noticed that some people seem to have problems with bitwise operators, so I decided to write this brief tutorial on how to use them.

An Introduction to bits

Bits, what are they you may ask?

Well, simply put, bits are the individual ones and zeros that make up every thing we do with computers. All the data you use is stored in your computer using bits. A BYTE is made up of eight bits, a WORD is two BYTEs, or sixteen bits. And a DWORD is two WORDS, or thirty two bits.

 0 1 0 0 0 1 1 1 1 0 0 0 0 1 1 1 0 1 1 1 0 1 0 0 0 1 1 1 1 0 0 0
||              |               |               |              ||
|+- bit 31      |               |               |       bit 0 -+|
|               |               |               |               |
+-- BYTE 3 -----+--- BYTE 2 ----+--- BYTE 1 ----+-- BYTE 0 -----+
|                               |                               |
+----------- WORD 1 ------------+----------- WORD 0 ------------+
|                                                               |
+--------------------------- DWORD -----------------------------+

The beauty of having bitwise operators is that you can use a BYTE, WORD or DWORD as a small array or structure. Using bitwise operators you can check or set the values of individual bits or even a group of bits.

Hexadecimal numbers and how they relate to bits

When working with bits, it is kind of hard to express every number using just ones and zeros, which is known as binary notation. To get around this we use hexadecimal (base 16) numbers.

As you may or may not know, it takes four bits to cover all the numbers from zero to fifteen, which also happens to be the range of a single digit hexadecimal number. This group of four bits, or half a BYTE, is called a nibble. As there are two nibbles in a BYTE, we can use two hexadecimal digits to show the value of one BYTE.

NIBBLE   HEX VALUE
======   =========
 0000        0
 0001        1
 0010        2
 0011        3
 0100        4
 0101        5
 0110        6
 0111        7
 1000        8
 1001        9
 1010        A
 1011        B
 1100        C
 1101        D
 1110        E
 1111        F

So if we had one BYTE containing the letter 'r' (ASCII code 114) it would look like this:

0111 0010    binary
  7    2     hexadecimal

We could write it as '0x72'

Bitwise operators

There are six bitwise operators. They are:
   &   The AND operator
   |   The OR operator
   ^   The XOR operator
   ~   The Ones Complement or Inversion operator
  >>   The Right Shift operator
  <<   The Left Shift operator.

The & operator

The & (AND) operator compares two values, and returns a value that has its bits set if, and only if, the two values being compared both have their corresponding bits set. The bits are compared using the following table

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

An ideal use for this is to set up a mask to check the values of certain bits. Say we have a BYTE that contains some bit flags, and we want to check if bit four bit is set.

BYTE b = 50;
if ( b & 0x10 )
    cout << "Bit four is set" << endl;
else
    cout << "Bit four is clear" << endl;

This would result in the following calculation

    00110010  - b
  & 00010000  - & 0x10
  ----------
    00010000  - result

So we see that bit four is set.

The | operator

The | (OR) operator compares two values, and returns a value that has its bits set if one or the other values, or both, have their corresponding bits set. The bits are compared using the following table

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

An ideal use for this is to ensure that certain bits are set. Say we want to ensure that bit three of some value is set

BYTE b = 50;
BYTE c = b | 0x04;
cout << "c = " << c << endl;

This would result in the following calculation

    00110010  - b
  | 00000100  - | 0x04
  ----------
    00110110  - result

The ^ operator

The ^ (XOR) operator compares two values, and returns a value that has its bits set if one or the other value has its corresponding bits set, but not both. The bits are compared using the following table

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

An ideal use for this is to toggle certain bits. Say we want toggle the bits three and four

BYTE b = 50;
cout << "b = " << b << endl;
b = b ^ 0x18;
cout << "b = " << b << endl;
b = b ^ 0x18;
cout << "b = " << b << endl;

This would result in the following calculations

    00110010  - b
  ^ 00011000  - ^ 0x18
  ----------
    00101010  - result

    00101010  - b
  ^ 00011000  - ^ 0x18
  ----------
    00110010  - result

The ~ operator

The ~ (Ones Complement or inversion) operator acts only on one value and it inverts it, turning all the ones int zeros, and all the zeros into ones. An ideal use of this would be to set certain bytes to zero, and ensuring all other bytes are set to one, regardless of the size of the data. Say we want to set all the bits to one except bits zero and one

BYTE b = ~0x03;
cout << "b = " << b << endl;
WORD w = ~0x03;
cout << "w = " << w << endl;

This would result in the following calculations

    00000011  - 0x03
    11111100  - ~0x03  b

    0000000000000011  - 0x03
    1111111111111100  - ~0x03  w

Another ideal use, is to combine it with the & operator to ensure that certain bits are set to zero. Say we want to clear bit four

BYTE b = 50;
cout << "b = " << b << endl;
BYTE c = b & ~0x10;
cout << "c = " << c << endl;

This would result in the following calculations

    00110010  - b
  & 11101111  - ~0x10
  ----------
    00100010  - result

The >> and << operators

The >> (Right shift) and << (left shift) operators move the bits the number of bit positions specified. The >> operator shifts the bits from the high bit to the low bit. The << operator shifts the bits from the low bit to the high bit. One use for these operators is to align the bits for whatever reason (check out the MAKEWPARAM, HIWORD, and LOWORD macros)

BYTE b = 12;
cout << "b = " << b << endl;
BYTE c = b << 2;
cout << "c = " << c << endl;
c = b >> 2;
cout << "c = " << c << endl;

This would result in the following calculations

    00001100  - b
    00110000  - b << 2
    00000011  - b >> 2

Bit Fields

Another interesting thing that can be done using bits is to have bit fields. With bit fields you can set up minature structures within a BYTE, WORD or DWORD. Say, for example, we want to keep track of dates, but we want to use the least amount of memory as possible. We could declare our structure this way

struct date_struct {
    BYTE day   : 5,   // 1 to 31
         month : 4,   // 1 to 12
         year  : 14;  // 0 to 9999
    } date;

In this example, the day field takes up the lowest 5 bits, month the next four, and year the next 14 bits. So we can store the date structure in twenty three bits, which is contained in three BYTEs. The twenty fourth bit is ignored. If I had declared it using an integer for each field, the structure would have taken up 12 BYTEs.

|0 0 0 0 0 0 0 0|0 0 0 0 0 0 0 0|0 0 0 0 0 0 0 0|
  |                           |       |         |
  +------ year ---------------+ month +-- day --+

Now lets pick this declaration apart to see what we are doing.

First we will look at the data type we are using for the bit field structure. In this case we used a BYTE. A BYTE is 8 bits, and by using it, the compiler will allocate one BYTE for storage. If however, we use more than 8 bits in our structure, the compiler will allocate another BYTE, as many BYTEs as it takes to hold our structure. If we had used a WORD or DWORD, the compiler would have allocated a total of 32 bits to hold our structure.

Now lets look at how the various fields are declared. First we have the variable (day, month, and year), followed by a colon that separates the variable from the number of bits that it contains. Each bit field is separated by a comma, and the list is ended with a semicolon.

Now we get to the struct declaration. We put the bit fields into a struct like this so that we can use convention structure accessing notation to get at the structure members. Also, since we can not get the addresses of bit fields, we can now use the address of the structure.

date.day = 12;

dateptr = &date;
dateptr->year = 1852;

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

PJ Arends
President
Canada Canada
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralRe: Good place to startmemberant-dog11 Jan '08 - 4:38 
so do I!
 
I'm eager to learn more!

GeneralRe: Good place to startmemberxlyz.os31 Oct '09 - 20:47 
Just a scenario
GeneralGoodmemberLeeSharp15 Jun '06 - 5:37 
It's really a good article!Big Grin | :-D
 
努力,什么都有!
GeneralVery Nicemembervijayakumar_akm10 May '05 - 17:15 
Thanks for this ArticleBig Grin | :-D
Generalnice article ,u can add how bitwise ops can be combinedsussAnonymous20 Sep '04 - 0:07 
for example what means
a = b >> c & d
a = b >> c | d
QuestionCan i use the declaration?membermahmutarik22 Mar '04 - 3:32 

I try to make a program that sets, clears a bit...
 
Can i provide a mechanism to use an array of BitVectors each with length l and size n? Can i use the declaration:
 
BitVector d(l)[n] ?

Generalerror C2034: 'year' : type of bit field too small for number of bitsmemberLee McKenzie13 Mar '04 - 3:50 
error C2034: 'year' : type of bit field too small for number of bits
 
I get this error when I compile when using the following struct
 
struct date_struct {
BYTE day : 5, // 1 to 31
month : 4, // 1 to 12
year : 14; // 0 to 9999
} date;
 
but when I use this struct it works..
 
struct date_struct {
BYTE day : 5, // 1 to 31
month : 4, // 1 to 12
year : 8; // 0 to 9999
} date;
 
it appears that you can't have 14 bits for the year because the BYTE is only 8 bits large...I am using msvc 6 ... apparently no one else gets this error?
 
also I am declaring byte as
 
typedef unsigned char BYTE;
 
thanks
GeneralRe: error C2034: 'year' : type of bit field too small for number of bitsmemberleekillestein17 Jun '04 - 11:52 
Hi,
 
if you lose the delarator the structure becomes a C style structure and will work fine!
 
struct date_struct
{
BYTE day : 5, // 1 to 31
month : 4, // 1 to 12
year : 8; // 0 to 9999
};
 
hope this helps
 
Poke tongue | ;-P
GeneralRe: error C2034: 'year' : type of bit field too small for number of bitsmemberJohn R. Shaw14 May '07 - 18:49 
I was wondering when someone would notice that, besides me. I did not compile it but your result is exactly what should have happened.
 
If you changed BYTE to DWORD, then the compiler should automatically pack it to 4 bytes, which is the number of bytes required to hold the data, and will produce the bit pattern in the article (if you convert ‘date’ to ‘DWORD’).
 
Example: DWORD n = *(DWORD*)&date;
 
The combination BYTE day:5, month:4; WORD year:14; will also produce 4 bytes, but the bit pattern will not be the same as presented in the article, when converted to DWORD.
 
In either case you can not use 8 bits for the year, because then you could not use a year higher than 255.
 
Bit fields are a bit tricky if the bit pattern needs to be known.

 
INTP
"Program testing can be used to show the presence of bugs, but never to show their absence."Edsger Dijkstra

GeneralRe: error C2034: 'year' : type of bit field too small for number of bitsmemberislandscape14 Oct '09 - 3:03 
i got the same problem ..
GeneralNice Articlemembersaurabhbajaj27 Nov '03 - 22:17 
Hi Guys,a very nice article for beginners.Smile | :)
GeneralI Get something in this articlememberAfxMessageBox8 Sep '03 - 3:44 
I Learned Bit Fields
 
To Be the Best!
GeneralTKSmemberxhight2 Sep '03 - 8:16 
It's very good.
But i don't understand "BYTE b = 50".
This will not compile .
 

error:
 
identifier 'b';

 
study,work and thought
GeneralRe: TKSmemberPJ Arends2 Sep '03 - 8:33 
you have to either add #include <windows.h> or typedef unsigned char BYTE to your file so that the compiler knows what a BYTE is.
 
HTH
 






Sonork 100.11743 Chicken Little
 
"You're obviously a superstar." - Christian Graus about me - 12 Feb '03
 
Within you lies the power for good - Use it!
GeneralGreatmemberMessnerEW23 Jul '03 - 20:36 
Great job. Better than all the books from "Microsoft"
or wathever.
Keep on with that.
GeneralRe: Greatmembermanzoor1012 Dec '08 - 4:12 
I'm programming for more than 4 months and haven't understood them fully, thanks for this article it removed few of my confusions.
 
http://www.webhostingyes.com/

Generalwe did it againmemberJeremy Falcon25 Jun '03 - 5:33 
Well, looks like we have similar articles again! Great minds think alike, eh? Smile | :)
 
Anyway, since I believe that our articles complement one another, would you want to cross-link them? Also, would you mind if I referenced your article in my future ones that will be meant to build upon my Learning Binary and Hexadecimal one?
 
BTW, if you'd like, I'd also be willing to make a downloadable PDF version for you. I have Acrobat 5 at home and just look for excuses to use it. Smile | :)
 
Jeremy Falcon
GeneralRe: we did it againmemberPJ Arends25 Jun '03 - 6:54 
Jeremy Falcon wrote:
Well, looks like we have similar articles again! Great minds think alike, eh?
 
Yeah, but mine was first this time;P
 
Jeremy Falcon wrote:
Anyway, since I believe that our articles complement one another, would you want to cross-link them? Also, would you mind if I referenced your article in my future ones that will be meant to build upon my Learning Binary and Hexadecimal one?
 
You can reference this one from your articles if you like, but I will consider your note here to be a cross reference to your article. (I'm too damn lazy to bother updating my article)
 
Jeremy Falcon wrote:
BTW, if you'd like, I'd also be willing to make a downloadable PDF version for you. I have Acrobat 5 at home and just look for excuses to use it.
 
It is nice of you offer, but until PDF versions of articles becomes the CP standard I will pass. But do feel free to make a PDF copy for your own personal use if you desireSmile | :)
 






Sonork 100.11743 Chicken Little
 
"You're obviously a superstar." - Christian Graus about me - 12 Feb '03
 
Within you lies the power for good - Use it!
GeneralNibble SwapsussBcdarus1 Jun '03 - 18:36 
Hi,
I want to know how to swap nibbles in a byte without using another variable.
 
For example,
 
X = 0xAF,
 
then swapped value should be
X = 0xFA
 
And this should be done without using any other variable.
 
Regards,
Bcdarus
GeneralRe: Nibble SwapmemberPJ Arends2 Jun '03 - 5:40 
I don't do other peoples homework, but I will give you a clue: You need to use the &, |, <<, and >> operators.
 
If you post some code you already have, I may be able to help you some moreSmile | :)
 






Sonork 100.11743 Chicken Little
 
"You're obviously a superstar." - Christian Graus about me - 12 Feb '03
 
Within you lies the power for good - Use it!
GeneralASL vs ROLmemberKochise25 Jun '03 - 5:21 
In Motorola assembly, try :
 
move.b  #$AF,D0
asl.b   #4,D0
 
and
 
move.b  #$AF,D0
rol.b   #4,D0
 
The first returns $F0 (puts four 0 bits from the right), the second returns $FA (the outputing four $F bits from the left are reinserted from the right)...
 
In C, when using <<, it's like using ASL (Arithmetic Shift Left). There is nothing like ROL (ROtate Left) in C...
 
Use also something like :
 
unsigned char nSwap;
nSwap = 0xAF;
nSwap = (nSwap << 4) || (nSwap >> 4); // 0xF0 || 0x0A
 
Kochise
 
In Cod we trust !
GeneralRe: ASL vs ROLmemberPJ Arends25 Jun '03 - 6:40 
The code I was thinking of is:
x = ((x & 0x0F) << 4) | ((x & 0xF0) >> 4);
I don't think your use of the logical-OR ( || ) operator will work, but I have not tried it;)
 






Sonork 100.11743 Chicken Little
 
"You're obviously a superstar." - Christian Graus about me - 12 Feb '03
 
Within you lies the power for good - Use it!
GeneralMistyping...memberKochise25 Jun '03 - 22:49 
Yeap, |, not || Wink | ;)
 
Just add that the '& 0x0F' and '& 0xF0' are useless if you work on a char (8 bits bounding)
 
Kochise
 
In Cod we trust !
GeneralRe: Mistyping...memberRyan Binns30 Jun '03 - 23:02 
Kochise wrote:
Just add that the '& 0x0F' and '& 0xF0' are useless if you work on a char (8 bits bounding)
 
Not quite. It depends on whether the compiler performs a logical right-shift or an arithmetic right-shift. For instance:
 
0xfa >> 4 using a logical shift gives 0x0f, as required
0xfa >> 4 using an arithmetic shift gives 0xff, certainly not wanted.
 
Logical shifts shift in zero as the top bit. Arithmetic shifts copy the old top bit as the new top bit, as can be seen in the above example.
 
There is no requirement for the compiler to choose one over the other, so leaving out the '&' bit is just asking for trouble. I believe it works with the MS compiler, but I'm not sure about any others.
 
Ryan
 
Being little and getting pushed around by big guys all my life I guess I compensate by pushing electrons and holes around. What a bully I am, but I do enjoy making subatomic particles hop at my bidding - Roger Wright (2nd April 2003, The Lounge)

Punctuality is only a virtue for those who aren't smart enough to think of good excuses for being late - John Nichol "Point Of Impact"

GeneralRe: Mistyping...memberKochise1 Jul '03 - 0:26 
Right, but the QNX GCC and the Lattice C compilers do the same, ONLY logical shift... Even in the GreenLeaf compiler there is NO arithmetic shift !
 
But true, to be sure whet is done, the best is to force the '& 0x0F' and '& 0xF0' in every case !
 
Kochise
 
In Cod we trust !

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130516.1 | Last Updated 9 May 2002
Article Copyright 2002 by PJ Arends
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid