<

Binary Bit Flags: Tutorial and Usage Tips

Published on
23,986 Points
12,886 Views
16 Endorsements
Last Modified:
Awarded
DanRollins
If you need to save a true/false value -- in any programming language -- you can create a Boolean variable.  But it is often possible to pack a whole bunch of Boolean values into a single numeric value.  You've probably seen code sequences like these:
if (x & VK_SHIFT) { // Shift key pressed
   ...
dwExStyle= WS_EX_TOOLWINDOW OR WS_EX_TOPMOST
   ...
if (window.event.button & 1)  {  // left mouse button pressed...
   ...
if ((window.event.button & 3) == 3)  {  // left and right mouse button pressed...
   ...
if ((window.event.button & 3) != 0 )  {  // either left OR right button pressed...
   ...
treeView1.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Bottom;

Open in new window

... and wondered what is going on.  In this article, I hope to enlighten you, or at least pass on enough information to make you dangerous :-)  

Pros and Cons of Using Binary Bit Flags:

Pros:
Efficient: Very small storage space to hold a lot of information.
Efficient: Ultra-fast (actually fastest possible) for making programmatic decisions, especially when looking for combinations of attributes or options.
Efficient: Concise storage means fast data transfer.
Extensible:  New code does not "break" old code.  Additional (new) data can be packed into the same variable without causing backward compatibility problems.
Extensible:  New data does not require new database schema or changed record layouts.  Only the program code changes.
Cons:
Idiomatic.  Not all programmers understand what's going on.  Possible increased documentation requirements.

Obligatory Anecdote:
The other day, I was on a conference call with a tableful of software developers, and I found myself needing to explain the concept of binary bit flags.  

"Yes," I said. "The value is usually 545.  But you can't just compare to 545.  It's a set of bit flags."

"Huh???  So what does 545 mean?"

"Just a sec, let me check... 545 is a combination of 512, 32, and 1, so it means 'Has been processed', 'Needs to be printed', and 'Is tagged for automatic export' ... the flag values are listed in Appendix B."

There was a sort of stunned silence on the other end of the phone line, then an uncomfortable chuckle (I imagine that puzzled glances were being exchanged), and eventually... "Oh, I get it,... It's one of those see plus plus things..."

I mentally clawed around for a way to respond... I couldn't really educate the entire group, so I finally ended up saying, "For your purposes, in this situation, you can just check to make sure that the value is greater than or equal to 512.  I'll explain the details in an email."

Are Binary Bit Flags no longer covered in school?  
I think they must be, after all, you can't use some of the most important Windows API calls, such as CreateWindow, CreateFile, SetWindowPos, and so forth, without knowing how to combine "window style codes" or "file access options."  Every programming language exposes binary operations, so one would think students would hear about it again and again.  But perhaps there is less emphasis these days, or perhaps students all sleep through those classes.

It certainly is "easier" to just create a dozen individual Boolean variables.  And it certainly is easier to teach students how to use simple Boolean values.  There is no shortage of disk space or RAM on modern computers, so the need for efficiency is not as pressing as it once was.  As for data transfer capability... HTML is one of the first things taught these days and it is about as inefficient a method of data transfer as imaginable.  Consider:
<INPUT TYPE=checkbox CHECKED ID=chk1> ...etc...

Open in new window

To indicate one of the two possible options (checked or not checked) we blithely use 16 bytes of UNICODE text to spell out the entire word " CHECKED".  That is 256 bits where (using some hypothetical more efficient format) you could get by with a single bit.  XML is also unbelievably inefficient as a data transfer mechanism, but let's not go there.  I'm not here to malign HTML or XML, I'm just illustrating a "state of mind" that is common these days.  It seems to me that when there's an opportunity for a hundredfold increase in efficiency, it's something you ought to explore.

Recognizing Binary Bit Flags
You know that a class member or a structure or database field is actually a combination of binary bit flags when:

The word "flags" or "mask" appears in the member name or the placeholder parameter name.  For instance, dwStyleFlags, or nOptionMask, or dwFlagsAndAttributes.
There is an associated set of #define values or an enumeration that contains values that are all exact powers of 2:

   1, 2, 4, 8, 16, 32 ... or
   0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, ... etc.
If the documentation says "You can combine these using the bitwise OR function."

You may not even know that you are using binary flags.  For instance, this Microsoft documentation page shows the following list to describe the possible values of a return code:

   0  Default. No button is pressed.
   1  Left button is pressed.
   2  Right button is pressed.
   3  Left and right buttons are both pressed.
   4  Middle button is pressed.
   5  Left and middle buttons both are pressed.
   6  Right and middle buttons are both pressed.
   7  All three buttons are pressed.

After you've read this article, come back here and see if you know what's really going on.  See if you can answer the question: "Is the right button NOT pressed?" with a single program statement.

Background on the Ones and the Zeros
In the computer's memory a numeric value like, say, 162 is actually a series of 1s and 0s.  It can be displayed as:
  162              (decimal)
  10100010  (binary)
  A2                 (hexadecimal)

How do I know that?  I use the Calculator that comes with Windows!
The Magic of Calc.ExeType in the decimal value, then click the Hex or Bin radio buttons.  If your calculator does not show those options, then select View>Programmer from the menu.

Let's take a close look at the bits:
bits of a byteBinary is really quite similar to decimal.  If you see a decimal number like 7,023 you know that there is a 7 in the thousands place, a 2 in the tens place, and a 3 in the ones place.   You add it up without even thinking:
   7 in the 1000s place = 7000
   0 in the 100s place=        +0
   2 in the 10s place=        +20
   3 in the 1s place=            +3
   Total                               7023

It's the same thing with binary, except that instead of powers of ten, the positions indicate powers of two.  

In our example, 10100010, there are 1s in the 128s place, the 32s place, and in the 2s place.  So let's verify the conclusion reached by Calc.Exe:
  1 in the 128s place = 128
  0 in the 64s place=       +0
  1 in the 32s place=     +32
  0 in the 16s place=       +0
  0 in the  8s place=        +0
  0 in the  4s place=        +0
  1 in the  2s place=        +2
  0 in the  1s place=        +0
  Total                              162

All you need to remember is the sequence 1, 2, 4, 8, 16, 32, 64, 128.  If you forget one of those, just take the previous one that you do remember and multiply it by 2.

Whatz the dill with Hex?
Hexadecimal is really just "quasi-binary." It's a shorthand way to express a number when you are really working with the 1s and the 0s.  It puts the bits into little groups of four for easier analysis.

Any group of four bits can represent a value between 0 and 15, inclusive.  The first ten values are the relatively-easy-to-remember digits 0 through 9, then A, B, C, D, E, and F (like in the first part of that song).  A way to help you remember the values of the letters:
         "13 is an unlucky grade to get." (13 is D).
1010     is 8+2 = 10 (A in hex)
    0010 is 2        (2 in hex)
10100010 is A2 in hex.

Open in new window

Now after reading that, you can take any numeric value and turn it into a set of 1s and 0s.  

We've only looked at the first eight bits, but the exact same thinking works for larger values: 16 bits, 32 bits, and so forth.  The next "place" above 128 is 256, then 512, then 1024, .... all the way up to 0x80000000 (you can use Calculator to find the decimal value of that, if you care).

With all of that out of the way, let's move on to the actual topic of this article.

Binary Operations:
Most programming languages (every one that I've ever used) provide a direct means to manipulate binary bit flags.  With ASM, C/C++, C#, Java, JavaScript, SQL, et. al, there is a special set of operators.  With some other languages, including BASIC, it is usually an "overload" of the AND and OR functions that are familiar from their use in IF/THEN constructs.  

AND
Combines the 1s and 0s such that where there is a 1 in both values, there is a 1 in the result.
z= x AND y  (Visual Basic)
z= x & y    (most other languages)

    10001000
AND 00001111
    00001000 (result contains a 1 only where both operands have a 1)

Open in new window

OR
Combines the 1s and 0s such that where there is a 1 in either value, there is a 1 in the result.
z= x OR y (Visual Basic)
z= x | y  (most other languages) 

    10001000
 OR 00001111
    10001111 (result contains the 1s from both operands)

Open in new window

NOT
The result is created by toggling (making the opposite) each of the bits in the single operand.
z= NOT x (Visual Basic)
z= ~ x   (most other languages)

NOT 10000111
    01111000  (result contains a toggling of all of the bits; 1->0, 0->1)

Open in new window


That's all you really need to know.  There is an operation called XOR that you probably won't need to use and there are shift and rotate operations that you can read about in some textbook if you are really curious.

The AND operation is the real key to using bit flags.  It is a filtering or masking operation.  Like a Halloween mask, it hides some or all of what is beneath.  Let's say that, for whatever reason, you don't care about the upper four bits of a binary value.  You could obtain a value that contains only the lower four bits by using AND:
    10101001 (the starting value) 
AND 00001111 (the mask, low four bits set to 1)
    00001001 (result contains only data from the lower four bits)

Open in new window


Using Binary Bit Flags
==================

Testing Bit Values
When using binary bit flags, it is most common to want to set or find the value of a single bit.  So if you need to know whether, say, the third lowest bit is set, you would use code like:
if ( (dwFlags & 4 ) == 4 ) { 
    ... the third lowest bit is set
}
if ( (dwFlags & 4) == 0 ) { 
    ... the third lowest bit is clear (not set)
}

Open in new window


But you don't want to just use a bare value like 4.  You will certainly want to create symbolic constants to make your code self-documenting.  In C++, you might go with something like this:

//---------------------------- bit flags for dwCarOptFlags
#define COF_None        0x0000
#define COF_Hatchback   0x0001
#define COF_SunRoof     0x0002
#define COF_FourDoor    0x0004
#define COF_AmRadio     0x0008
#define COF_8TrackTape  0x0010
...

if ( rCar.dwCarOptFlags & COF_AmRadio ) {
    ... the car has a radio
}

Open in new window


Testing for Multiple Bits
One of the reasons you might use this type of construct is that it provides a cool way to find out several things at once.  For instance, using the above "Car option Flags," you could see if a car has both a sun roof and a radio in a single statement:
int nSearchMask= (COF_SunRoof | COF_AmRadio);

if ( (rCar.dwCarOptFlags & nSearchMask) == nSearchMask ) {
    ... the car has BOTH a radio and a sun roof
}

Open in new window

You can see if a car has neither option with this statement:
if ( (rCar.dwCarOptFlags & nSearchMask) == 0 ) {
    ... the car has NEITHER a radio NOR a sun roof
}

Open in new window

You can see if a car has either option with this statement:
if ( (rCar.dwCarOptFlags & nSearchMask) != 0 ) {
    ... the car has EITHER a radio OR a sun roof or both
}

Open in new window

Note that when using multiple-bit masks, it's usually important to do the AND, and then compare the result to the mask value.  You will often see, for instance:
if ( rCar.dwCarOptFlags & nSearchMask ) { // valid, but avoid this syntax
    ... the car has EITHER a radio OR a sun roof or both
}

Open in new window

...but that is a shorthand/idiom for the real question being asked, which is:
if ( (rCar.dwCarOptFlags & nSearchMask) != 0) { // preferred syntax
    ... the car has EITHER a radio OR a sun roof or both
}

Open in new window

That's because an if statement evaluates its single operand and branches based on whether that value is non-zero.  In the case of a binary AND operation, the result is non-zero when any of the mask bits are set in the result.  It's best to ask the question specifically, adding the (... != 0) if that's what you need to know.

Here's a SQL query that finds all cars that have both a hatchback and a sun roof (mask value is 0x0003, in our example):
SELECT * FROM Cars WHERE (CarOptFlags & 3)=3;

Open in new window

If CarOptFlags is an indexed field, that will be a very fast query.

Setting Bit Flag Values
Now that you can test/query a binary bit flag field, you need to know how to set individual bit-flag values into it.  If you want to initialize it from scratch, that's easy:
rCar.dwCarOptFlags= COF_None;                  // no options
rCar.dwCarOptFlags= COF_SunRoof | COF_AmRadio; // two options

Open in new window

The harder task is to set a single flag without altering the other ones.  Here's the sequence:
rCar.dwCarOptFlags= rCar.dwCarOptFlags OR COF_AmRadio // VB

rCar.dwCarOptFlags= rCar.dwCarOptFlags | COF_AmRadio; // C
rCar.dwCarOptFlags |= COF_AmRadio;    // C |= operator idiom

Open in new window


Clear a Bit Flag Value
One day, you find out that the radio does not work in one of the cars, so you need to remove that flag from the record.  The task is to take the current value and force a 0 into fourth bit position (represented by the COF_AmRadio constant).  We've seen how to force a 1 into that position (just use the OR operator), but to clear a bit, you need to use a different technique.  You need to mask the current value, using a mask that has 1s in every position except the one of interest.  Here's where that binary NOT operator comes into play.  It lets us create the desired mask easily:

'                   COF_AmRadio is 00001000
'VB:
nNoRadioMask =  NOT COF_AmRadio  ' 11110111
rCar.dwCarOptFlags= rCar.dwCarOptFlags AND nNoRadioMask

// C/C++/C#, et al.:
int nNoRadioMask= ~COF_AmRadio; // 11110111
rCar.dwCarOptFlags &= nNoRadioMask;

Open in new window


Write a function!
The people who maintain your code might get a glazed look in the eye when you start talking in "binod" to them.  So make it easy for them.  Provide a utility function like this:
DWORD ModifyCarOptFlags( CarRec& rCar, DWORD dwAdd, DWORD dwRemove ) 
{
    DWORD nFlags= rCar.dwCarOptFlags;
    nFlags &= ~dwRemove;
    nFlags |= dwAdd;
    rCar.dwCarOptFlags= nFlags;
    return( nFlags );
}

Open in new window

If you are working in an object-oriented programming language (and who isn't?) then you can provide easy-access functions to turn on or off any of the bits...
class CCarRec
{ 
bool CCarRec::get_HasSunRoof() { return (dwCarOptFlags & COF_SunRoof) == COF_SunRoof); };
void CCarRec::set_HasSunRoof() { dwCarOptFlags |= COF_SunRoof; };
...
}

Open in new window

Another thing I've done with bit flags is create a "toString" function like this:
CString CCarRec::OptsAsString() 
{
    CString sRet="";
    if ( dwCarOptFlags & COF_Hatchback) sRet += "Hatchback ";
    if ( dwCarOptFlags & COF_SunRoof  ) sRet += "SunRoof ";
    if ( dwCarOptFlags & COF_FourDoor ) sRet += "FourDoor ";
    if ( dwCarOptFlags & COF_AmRadio  ) sRet += "AmRadio ";
    ... etc...
    return sRet;
}

Open in new window

The idea is that even if the future code maintainer hasn't read this article, he can still do a string search or an eyeball-check to know what option flags are set.


More About the Advantages
At the beginning of this article, I made certain assertions about how efficient and extensible it is to use binary bit flags.  Before I wrap this up, I'd like to justify some of what I said.

Efficient: Fast
A binary AND operation translates directly to an AND opcode in machine code.  If you ever look at CPU specs, you'll find that AND, OR, and other binary operations are not just among the fastest, but are actually the fastest executing opcodes.  It relates to the fact that virtually every other operation is built on these operations.

Extensible:  Program changes are backwardly compatible.
You may have noticed that one of the bit flags we've used was named COF_8TrackTape.  This reflects something I actually saw when working on a legacy system for a used car dealer.  The module header file had a section that looked something like this:

#define COF_Hatchback         0x0001
#define COF_SunRoof           0x0002
#define COF_FourDoor          0x0004
//#define COF_AmRadio           0x0008
#define COF_AmFmRadio         0x0008  // redefined 1977 [LLR]
#define COF_8TrackTape        0x0010  
...
#define COF_CassetPlyr        0x0080  // added 1977 [LLR]
#define CTF_IsDiesel          0x0100  // added 1979 [LLR]
...
#define COF_CDPlyr            0x1000  // added 1989 [LLR]
...
#define CTF_IsHybrid      0x04000000  // added 2006 [JM]
#define CTF_HasBluetooth  0x08000000  // added 2009 [DR]

Open in new window

From the comments, I could see that the flags variable had started out as a 16-bit word, then evolved into a 32-bit value over time (it was still defined as an unsigned int).  The point is that the same field started out containing information on just a handful of options, and over the course of many years, now identifies 28 different things -- and there is room for four more -- all without changing the database schema.

See how this is backwardly compatible?  Here's the key:  An older program that accesses the field will use only the bits it knows about.  Newer versions of the program can access additional bits.  The older code considers the other bits as "undefined" and won't query or change them.  The result is that old code works without breaking.

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
If you liked this article and want to see more from this author,  please click the Yes button near the:
      Was this article helpful?
label that is just below and to the right of this text.   Thanks!
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
16
Comment
Author:DanRollins
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
4 Comments
 
LVL 40

Expert Comment

by:evilrix
Hey Dan,

I wonder if you'd consider writing some assembly for beginners articles. I, for one, would very much like to get my hands on such material. There is precious little on the web aimed at the absolutely noob like myself.

I know this is a specialist area for you so I thought I might be able to encourage you to push the ol' Rollin's magic pen in that direction :)

Best regards,

evilrix

PS. Very nice article Dan. Get's my "yes" vote above.
0
 
LVL 33

Expert Comment

by:pgnatyuk
Many thanks  for your articles.

"Oh, I get it,... It's one of those see plus plus things..."
:)
0
 
LVL 60

Expert Comment

by:Kevin Cross
Very nice, sir.  Thanks!
Voted yes above.
0
 
 

Administrative Comment

by:tigermatt
Dan,

Following consultation with the other Page Editors, your article has been awarded Editor's Choice status.

Thanks for your contribution!

tigermatt
Page Editor
0

Featured Post

On Demand Webinar - Networking for the Cloud Era

This webinar discusses:
-Common barriers companies experience when moving to the cloud
-How SD-WAN changes the way we look at networks
-Best practices customers should employ moving forward with cloud migration
-What happens behind the scenes of SteelConnect’s one-click button

Join & Write a Comment

Keep in touch with Experts Exchange

Tech news and trends delivered to your inbox every month