How to decode bit flags
Principle
The idea is very simple :- Define a mask having the same size in bytes than the value to decode ( let’s call it “v” ) and set the bits to extract to 1 and the others to 0
- Apply a logical AND operation between it and v
- Then shift the result right n times where n is the index ( starting from 0 ) of the first significant bit.
Example
Let’s apply this method on a real example, taken from the CALIPSO 05kmCLay product. We will extract the Ice/Water Phase of a cloud layer. This information is defined in the “Feature_Classification_Flags” dataset, and the phase bit flags are set at indexes 5-6 ( starting from 0 ). For a complete description of the flags definition, please refer to the CALIPSO Data Products Catalog. Be careful, the bits numbering starts from 1 in this document. The value to decode is read in the file CAL_LID_L2_05kmCLay-Prov-V2-02.2009-01-01T00-07-47ZN.hdf. Its value for the layer number 3 of the profile 3481 is 39898, on 2 bytes ( 1001101111011010 in binary format ) Define the bits selection mask We set a 2 bytes long mask, with only the bits 5 and 6 set to 1. The mask will be, in binary format :
ibit | 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 | --------------------------------------------------------- mask | 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 | => 96 in decimal formatLogical AND between the mask and the value All bits set to 0 in the mask will be 0 in the result, and all the bits set to 1 will give the same in the result than in the value
value | 1 0 0 1 1 0 1 1 1 1 0 1 1 0 1 0 | AND mask | 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 | --------------------------------------------------------- result | 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 | => 64Shift right to remove unsignificant bits Now, to have only the value of the bits in red, the result is shifted right 5 times ( 5 is the number of unsignificant bits on the right )
Shift 1 time | 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 | Shift 2 times | 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 | ... Shift 5 times | 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 | => 2
Synopsis
The way to implement this is different for all languages, but the C/C++/python way is :flag = ( value & mask ) >> n
Source
Here is an implementation in PYTHON, as a command-line tool. It offers 2 ways to specify the bits to read :- by specifying the first bit position and the flag width
- by specifying explicitly the bit selection mask
# -*- coding: utf-8 -*- """ Description : ------------- Extracts the bits from a bitflag and prints it out in a binary format Usage : ------- The tool can be used in 2 ways : 1) by specifying the first bit position and the flag width python get_bitflag <flag-word> <1st-bit-index> <width> where : <flag-word> : the value that contains the bits to extract <first_bit> : the position of the first significant bit ( starting from 0 ) <flag_width> : the number of bits of the flag to read 2) by specifying explicitly the bit selection mask python get_bitflag <flag-word> <bitmask> where : <flag-word> : the value that contains the bits to extract <mask> : the bits selection mask All input values can be specified either in decimal ( by default ), hexadecimal or binary format. For using hexadecimal values, preceed the values by "0x" and for binary by "0b" Example : --------- REM : All the following forms are exactly equivalent and leads to 3. Only the base of the input values differs, and how the bits are selected --- flag selection by mask --- python get_bitflag.py 59 48 python get_bitflag.py 0x3b 0x30 python get_bitflag.py 0b111011 0b110000 All will print out 3 : it selects the values the bits number 4 and 5 ( starting from 0 ) --- flag selection by first bit and width --- python get_bitflag.py 59 4 2 python get_bitflag.py 0x3b 4 2 python get_bitflag.py 0b111011 4 2 All will print out 3 : it constructs the mask for selecting the 2 bits starting from index 4, ie 110000 Prerequisites : --------------- python >= 2.5 ; not tested but probably all versions Author : -------- CGTD-ICARE/UDEV Nicolas PASCAL ( nicolas.pascal-at-icare.univ-lille.fr ) License : --------- This file must be used under the terms of the CeCILL. This source file is licensed as described in the file COPYING, which you should have received as part of this distribution. The terms are also available at http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt History : --------- v1.0.0 : 2009/12/07 - Add comments, bad values checking, hexadecimal and binary inputs support v0.1.0 : 2009/12/04 - creation """ import sys # if set to true, print detailled intermediate results __DEBUG__=False def get_bitflag_by_range ( flag_word, first_bit, flag_width ) : """ @brief read the bit flag values by specifying the first bit position and the flag width @warning if flag_width is null, an exception is raised @param flag_word flag word where are stored the bits to extract @param flag_width the number of bits of the flag @return the bit flag value """ if flag_width <= 0 : raise ValueError ( "Invalid width value %d. Must be a strictly positive integer"%flag_width ) # construct the bitflag selection mask mask = get_mask ( first_bit, flag_width ) return get_bitflag_by_mask ( flag_word, mask ) def get_bitflag_by_mask ( flag_word, mask ) : """ @brief read the bit flag values by an explicit specification of the mask @param flag_word flag word where are stored the bits to extract @param mask the bits selection mask, specified as an integer @return the bit flag value """ # find the position of the first not null bit of the mask first_bit_pos = get_first_bit_pos ( mask ) # apply a binary AND between value and mask, then shift result ot first significant bit return ( ( flag_word & mask ) >> first_bit_pos ) def get_first_bit_pos ( v ): """ @brief return the position of the first significant ( ie not null ) bit of a value, by increasing weight @param v an integer value """ if v == 0 : return 0 bit_pos = 0 while ( ( v & 1 ) == 0 ) : bit_pos = bit_pos + 1 v = v >> 1 return bit_pos def get_mask ( first_bit, flag_width ) : """ @brief construct the mask of a bitfield specified by the position of the first significant bit and the width, in number of bits, of the flag to read @param first_bit position of the first significant bit @param flag_width number of bits, of the flag to read @return the mask value, as an integer matching the binary : "flag_width" number of 1 + " first_bit" number of 0" """ mask = 0 for exp in xrange( flag_width ): mask += 2 ** ( first_bit + exp ) return mask def to_bin_str ( v ): """ @brief return the binary representation of v as a string @param v an integer value @return the binary representation of v as a string """ if v : return to_bin_str ( v >> 1 ) + str ( v & 1 ) else : return "" def usage (): """ @brief build the script usage string @return the script usage as a string """ s = "" s += "Usage :\n" s += "\tpython get_bitflag <flag-word> <1st-bit-index> <width>\n\n" s += "where :\n" s += "\t<flag-word> : the value that contains the bits to extract\n" s += "\t<first_bit> : the position of the first significant bit ( starting from 0 )\n" s += "\t<flag_width> : the number of bits of the flag to read\n" s += "\nOr\n" s += "\tpython get_bitflag <flag-word> <bitmask>\n\n" s += "where :\n" s += "\t<flag-word> : the value that contains the bits to extract\n" s += "\t<bitmask> : the bits selection mask\n\n" s += "All input values can be specified either in decimal ( by default ), hexadecimal or binary format.\n" s += "For using hexadecimal values, preceed the values by \"0x\" and for binary by \"0b\"\n" return s def main () : """ @brief program entry point """ if len ( sys.argv ) == 3 : # --- read bits using an explicit mask --- # val = int ( sys.argv[1], 0 ) mask = int ( sys.argv[2], 0 ) flag = get_bitflag_by_mask ( val, mask ) if __DEBUG__ : # print all the details of the operation print "val = %16s\t => %d"%( to_bin_str ( val ), val ) print "mask = %16s\t => %d"%( to_bin_str ( mask), mask ) print "res = %16s\t => %d"%( to_bin_str ( flag ), flag ) else : # just print the result print flag elif len ( sys.argv ) == 4 : # --- read bits by specifying the 1st bit position and the flag width --- # val = int ( sys.argv[1], 0 ) first_bit = int ( sys.argv[2], 0 ) flag_width = int ( sys.argv[3], 0 ) flag = get_bitflag_by_range ( val, first_bit, flag_width ) if __DEBUG__ : # print all the details of the operation print "val = %16s\t => %d"%( to_bin_str ( val ), val ) print "bit_pos = %d width = %d"%( first_bit, flag_width ) mask = get_mask ( first_bit, flag_width ) print "mask = %16s\t => %d"%( to_bin_str ( mask), mask ) print "res = %16s\t => %d"%( to_bin_str ( flag ), flag ) else : # just print the result print flag else : print "Invalid Number of Arguments" print usage() if __name__ == "__main__": main()