Tuesday, 26 July 2016

Bit flags, Bit masks and why they are useful?

Bit flags, Bit masks and why they are useful? Explanation with examples.


First of all we want to know what bit flag is and what is bit mask, in this post I would like to give brief knowledge on bit flags and bit masks

Bit Flags: As of we know that we cannot access bits directly, So we have to use the bit-wise operators to set, unset, or query them.

Bit flags are the individual bits that are used to improve the efficiency of the storage.

In storage-intensive cases where we have lots of related Boolean options, it can be useful to “pack” 8 individual Boolean's into a single byte for storage efficiency purposes.

Why are bit flags useful?

Bit flags are used in two cases:

1) It is very much useful when you have many sets of identical bit flags.


Instead of a single ch_flags variable, consider the case where you have two ch_flags variables: ch_flags1 and ch_flags2, each of which can store 8 options. If you defined these as two separate sets of Boolean, you’d need 16 Boolean, and thus 16 bytes. However, using bit flags, the memory cost is only 10 (8 bytes to define the options, and 1 byte for each ch_flags variable). With 100 myflag variables, your memory cost would be 108 bytes instead of 800. The more identical variables you need, the more substantial your memory savings.

2) Imagine you had a function that could take any combination of 32 different options. One way to write that function would be to use 32 individual Boolean parameters:
void My_Fun(bool option1, bool bVAl2, bool bVAl3, bool bVAl4, bool bVAl5, bool bVAl6, bool bVAl7, bool bVAl8, bool bVAl9, bool bVAl10, bool bVAl11, bool bVAl12, bool bVAl13, bool bVAl14, bool bVAl15, bool bVAl16, bool bVAl17, bool bVAl18, bool bVAl19, bool bVAl20, bool bVAl21, bool bVAl22, bool bVAl23, bool bVAl24, bool bVAl25, bool bVAl26, bool bVAl27, bool bVAl28, bool bVAl29, bool bVAl30, bool bVAl31, bool bVAl32);
Then when you wanted to call the function with bVAls 10 and 32 set to true, you’d have to do so like this:
My_Fun(false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true);
This is ridiculously difficult to read (is that bVAl 9, 10, or 11 that’s set to true?), and also means you have to remember which parameters corresponds to which bVAl (is setting the edit flag the 9th, 10th, or 11th parameter?) It may also not be very performant, as every function call has to copy 32 booleans from the caller to the function. Instead, if you defined the function using bit flags like this:
void My_Fun(unsigned int bVAls);
Then you could use bit flags to pass in only the bVAls you wanted:
My_Fun(bVAl10 | bVAl32);
Not only is this much more readable, it’s likely to be more performing as well, since it only involves 2 operations (one bitwise OR and one parameter copy).

This is one of the reasons OpenGL opted to use bitflag parameters instead of many consecutive booleans.

Also, if you have unused bit flags and need to add bVAls later, you can just define the bit flag.

There’s no need to change the function prototype, which is good for backwards compatibility Here the individual bits are so called bit flags.

The below is the sample example which helps us to understand the basic example:
const unsigned char Val1 = 0x01; // hex for 0000 0001
const unsigned char Val2= 0x02; // hex for 0000 0010
const unsigned char Val3= 0x04; // hex for 0000 0100
const unsigned char Val4 = 0x08; // hex for 0000 1000
const unsigned char Val 5 = 0x10; // hex for 0001 0000
const unsigned char Val 6 = 0x20; // hex for 0010 0000
const unsigned char Val 7 = 0x40; // hex for 0100 0000
const unsigned char Val 8 = 0x80; // hex for 1000 0000
To query the data we have to use the flag which is declared below:
unsigned char ch_flags = 0; // all flags/options turned off to start
Using the above bit flag query the bit state
if (ch_flags & option4)
To turn a bit on
ch_flags |= option4; 
ch_flags |= option4 | option5; 
To turn a bit off, we can use bitwise AND:
ch_flags &= ~option4; // turn option 4 off
ch_flags &= ~option4 & ~option5; // turn option4 and 5 off. 
Now coming the concept of Bitset: The bitset functionality is available in standard library of C++. Note that the number of bits must be a compile time constant.
#include 
 std::bitset<8> bits; 
If desired, the bitset can be initialized with an initial set of values:
#include 
 
std::bitset<8> bits(option1 | option2) ; // start with option 1 and 2 turned on
std::bitset<8> morebits(0x3) ; // start with bit pattern 0000 0011 
Bitset provides four key functions

• test() - this function allows us to query whether a bit is a 0 or 1
• set()- this function allows us to turn a bit on (this will do nothing if the bit is already on)
• reset()- this function allows us to turn a bit off (this will do nothing if the bit is already off)
• flip()- this function allows us to flip a bit from a 0 to a 1 or vice versa.

All the above functions takes parameter of the bit position, that means which bit should be operated on, and the position of the right most bit is ‘ZERO’,increasing with each successive bit to the left.
#include 
#include  
const int Val_1 = 0;
const int Val_2 = 1;
const int Val_3 = 2;
const int Val_4 = 3;
const int Val_5 = 4;
const int Val_6 = 5;
const int Val_7 = 6;
const int Val_8 = 7;
 
int main()
{
    std::bitset<8> bits(0x2); 
    bits.set(Val_5);
    bits.flip(Val_6); 
    bits.reset(Val_6);
    std::cout << "Bit 4 value: " << bits.test(Val_5) << '\n';
    std::cout << "Bit 5 value: " << bits.test(Val_6) << '\n';
    std::cout << "All the bits: " << bits << '\n';
     return 0;
} 
In the above program the std::count function prints all the values of the bits in the bitset.and the initialization value of the bitset is considered as binary. Bit masks:

The principles for bit flags can be extended to turn on, turn off, toggle, or query multiple bits at once, in a bit single operation. When we bundle individual bits together for the purpose of modifying them as a group, this is called a bit mask.

Let’s take a look at a sample program using bit masks. In the following program, we ask the user to enter a number.
#include 
 
int main()
{
    const unsigned int lowMask = 0xF; // bit mask to keep low 4 bits (hex for 0000 0000 0000 1111)
 
    std::cout << "Enter an integer: ";
    int num;
    std::cin >> num;
 
    num &= lowMask; // remove the high bits to leave only the low bits
 
    std::cout << "The 4 low bits have value: " << num << '\n';
 
    return 0;
} 
Here is another example:
#include 
int main()
{
    const unsigned int Val_R = 0xFF000000;
    const unsigned int Val_G = 0x00FF0000;
    const unsigned int Val_B = 0x0000FF00;
    const unsigned int Val_Alp = 0x000000FF;
 
    std::cout << "Enter a 32-bit RGBA color value in hexadecimal (e.g. FF7F3300): ";
    unsigned int pixel;
    std::cin >> std::hex >> pixel; // std::hex allows us to read in a hex value
    unsigned char red = (pixel & Val_R) >> 24;
    unsigned char green = (pixel & Val_G) >> 16;
    unsigned char blue = (pixel & Val_B) >> 8;
    unsigned char alpha = pixel & Val_Alp;
 
    std::cout << "Your color contains:\n";
    std::cout << static_cast(red) << " of 255 red\n";
    std::cout << static_cast(green) << " of 255 green\n";
    std::cout << static_cast(blue) << " of 255 blue\n";
    std::cout << static_cast(alpha) << " of 255 alpha\n";
} 

No comments: