65.9K
CodeProject is changing. Read more.
Home

Easier bitwise Operations

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.46/5 (10 votes)

Dec 21, 2001

CPOL

2 min read

viewsIcon

151465

downloadIcon

763

A collection of templates that simplify the comprehension of bitwise operations

Introduction

I've been developing software using C/C++ for a very long time yet I still have to think twice when I encounter bitwise operations, let alone when I have to explain them to somebody else! For example:

    unsigned const STAT_ONE = 0x0001;
    unsigned const STAT_TWO = 0x0002;
    
    unsigned status = 0x0001;
    if(status & (STAT_ONE | STAT_TWO)) // Does this evaluate to true or false?
       DoSomething();

We all know that the preceding statement checks if both STAT_ONE and STAT_TWO bits are set then DoSomething() will be executed... or is it if either STAT_ONE or STAT_TWO bits are set? Let's see:

    0001 or   // STAT_ONE
    0010      // STAT_TWO
    ----
    0011 and  // STAT_ONE | STAT_TWO
    0001      // status
    ----
    0001      // status & (STAT_ONE | STAT_TWO)

Because the result is non-zero, then DoSomething() will be executed. I think this can be very tricky, especially if the expressions get too complex. Isn't the intent of the following code easier to understand and less error-prone?

    unsigned const STAT_ONE = 0x0001;
    unsigned const STAT_TWO = 0x0002;

    unsigned status = 0x0001;
    if(isAnyBitSet(status, STAT_ONE | STAT_TWO)) // This evaluates to true, bit
                                                 // STAT_ONE is set and bit STAT_TWO 
                                                 // isn't. Same as previous example
       DoSomething();
    if(areAllBitsSet(status, STAT_ONE | STAT_TWO)) // This evaluates to false, 
                                                   // bit STAT_ONE is set but bit 
                                                   // STAT_TWO isn't
       DoSomething();

Also it's very convenient to access a bit's value by its position like this:

    if(isBitSetByPos(status, 5))   
       bitClearByPos(status, 5);

All the functions provided in the BitTools.h header are inlined so there is no size or run-time speed tradeoffs to worry about.

Templates

Bitmask-based Functions

  • template <class T, class U>
    	bool isAnyBitSet(T value, U mask)

    Returns true if any of the bits in mask is set in value. Defined as: (value & mask) != 0

       1010 and  // value
       0110      // mask
       ----
       0010      // Non-zero: true
  • template <class T, class U>
    	bool areAllBitsSet(T value, U mask)

    Returns true if all the bits in mask are set in value. Defined as: (value & mask) == mask

       1010 and  // value
       1110      // mask
       ----
       1010      // 1010 == 1010: true
  • template <class T, class U>

    bool areAllBitsClear(T value, U mask)

  • Returns true if all the bits in mask are cleared in value. Defined as: (value & mask) == 0
       1010 and  // value
       0101      // mask
       ----
       0000      // Zero: true
  • template <class T, class U>
    	T setBits(T value, U mask)

    Returns value with the mask bits set. Defined as: value | mask

       1000 or   // value
       0110      // mask
       ----
       1110
  • template <class T, class U>
    	T setBitsExcept(T value, U mask)

    Returns value with all the bits set except the mask bits. Defined as: value | ~mask

       1001 not  // mask
       ----
       0110 or   // ~mask
       0001      // value
       ----
       0111
  • template <class T, class U>
    	T clearBits(T value, U mask)

    Returns value with the mask bits cleared. Defined as: value & ~mask

       1001 not  // mask
       ----
       0110 and  // ~mask
       1111      // value
       ----
       0110
  • template <class T, class U>
    	T clearBitsExcept(T value, U mask)

    Returns value with all the bits cleared except the mask bits. Defined as: value & mask

       0010 and  // value
       0110      // mask
       ----
       0010
  • template <class T, class U>
    	T setClearBits(T value, U add, U remove)

    Returns value with the add bits set and the remove bits cleared. Defined as: (value | add) & ~remove

       1101 or   // value
       0101      // add
       ----
       0111
    
       0001 not  // remove
       ---- 
       1110 and  // ~remove
       0111      // value | add
       ----
       0110
  • template <class T, class U, class V>
    	T setBits(T value, U mask, V set)

    Returns value with the mask bits set or cleared depending on the value of set.

Position-based Functions

  •  

    template <class T>
    	T setBitByPos(T value, unsigned char n)

    Returns value with the nth bit set. Defined as value | (1 << n)

  •  

    template <class T>
    	T clearBitByPos(T value, unsigned char n)

    Returns value with the nth bit cleared. Defined as value & ~(1 << n)

  •  

    template <class T>
    	bool isBitSetByPos(T value, unsigned char n)

    Returns true if value has the nth bit set. Defined as (value & (1 << n)) != 0

  •  

    template <class T>
    	bool isBitClearByPos(T value, unsigned char n)

    Returns true if value has the nth bit cleared. Defined as (value & (1 << n)) == 0

Conclusion

The templates contained in BitTools.h provide an easier and less error-prone way of expressing bitwise operations with no performance or size penalties compared to hand written code. I hope you find these functions as useful and easy to use as I did.