简体   繁体   中英

Bit masking in Python

I have a byte (from some other vendor) where the potential bit masks are as follows:

value1 = 0x01 value2 = 0x02 value3 = 0x03 value4 = 0x04 value5 = 0x05 value6 = 0x06 value7 = 0x40 value8 = 0x80

I can count on ONE of value1 through value6 being present. And then value7 may or may not be set. value8 may or may not be set.

So this is legal: value2 | value7 | value8 This is not legal: value1 | value3 | value7

I need to figure out whether value 7 is set, value8 is set, and what the remaining value is.

I have the following python code. Is there a more elegant way to do this?

value1 = 0x01
value2 = 0x02
value3 = 0x03
value4 = 0x04
value5 = 0x05
value6 = 0x06
value7 = 0x40
value8 = 0x80

def format_byte_as_bits(value):
    return format(value,'b').zfill(8)

def mask_bits_on_byte(byte,mask):
    inverse_of_mask = mask ^ 0b11111111
    return byte & inverse_of_mask

def parse_byte(byte):

    value7_set = byte & value7 == value7
    value8_set = byte & value8 == value8
    byte = mask_bits_on_byte(byte,value7)
    byte = mask_bits_on_byte(byte,value8)
    base_value = byte
    return value7_set,value8_set,base_value

# Example 1
byte = value3 | value7
value7_set,value8_set,base_value = parse_byte(byte)
print("base_value = "+str(base_value))
print("value7_set = "+str(value7_set))
print("value8_set = "+str(value8_set))
print()

# Output:
# base_value = 3
# value7_set = True
# value8_set = False

# Example 2
byte = value5
value7_set,value8_set,base_value = parse_byte(byte)
print("base_value = "+str(base_value))
print("value7_set = "+str(value7_set))
print("value8_set = "+str(value8_set))
print()

# Output:
# base_value = 5
# value7_set = False
# value8_set = False

# Example 3
byte = value1 | value7 | value8
value7_set,value8_set,base_value = parse_byte(byte)
print("base_value = "+str(base_value))
print("value7_set = "+str(value7_set))
print("value8_set = "+str(value8_set))

# Output:
# base_value = 1
# value7_set = True
# value8_set = True

EDIT - I LOVE stackoverflow. So many useful answers, so quickly! You guys are awesome! Wish I could mark all the answers. But I'll at least give everyone an up vote!

EDIT2 - Based on the answers below, the code is simplified to the following:

value1 = 0x01
value2 = 0x02
value3 = 0x03
value4 = 0x04
value5 = 0x05
value6 = 0x06
value7 = 0x40
value8 = 0x80

def parse_byte(byte):
    return byte & value7, byte & 0x80, byte & 7

# Example 1
byte = value3 | value7
value7_set,value8_set,base_value = parse_byte(byte)
print("base_value = "+str(base_value))
if value7_set: print("value7_set")
if value8_set: print("value8_set")
print()

# Example 2
byte = value5
value7_set,value8_set,base_value = parse_byte(byte)
print("base_value = "+str(base_value))
if value7_set: print("value7_set")
if value8_set: print("value8_set")
print()

# Example 3
byte = value1 | value7 | value8
value7_set,value8_set,base_value = parse_byte(byte)
print("base_value = "+str(base_value))
if value7_set: print("value7_set")
if value8_set: print("value8_set")
print()

Most of your value* constants aren't actually bit masks, only value7 and value8 are. I'd define another bit mask to extract the lower bits, so I would have three bit masks in total:

mask0 = 0x07
mask1 = 0x40
mask2 = 0x80

Now your function becomes

def parse_byte(byte):
    return byte & mask2, byte & mask1, byte & mask0

I did not convert the results to bool -- I don't see why this should be necessary. When checking the returned value with if , it will be implicitly converted to bool anyway.

Also note that

format(value,'b').zfill(8)

can be simplified to

format(value,'08b')

Given a value such as:

>>> x = 0b10001000

You can find out whether the top bits are set with:

>>> bit8 = bool(x & 0b10000000)
>>> bit7 = bool(x & 0b01000000)

To find which lower bit is set, use a dictionary:

>>> bdict = dict((1<<i, i+1) for i in range(6))
>>> bdict[x & 0b00111111]
4

You don't need the other two functions:

def parse_byte(byte):
    value7_set = byte & value7 == value7
    value8_set = byte & value8 == value8
    base_value =  byte & 7
    return value7_set,value8_set,base_value

It's a little verbose but perfectly fine. The only change I'd make is to simplify parse_byte:

def parse_byte(byte):

     value7_set = byte & value7 == value7
     value8_set = byte & value8 == value8
     base_value = mask_bits_on_byte(byte,value7 | value8)
     return value7_set,value8_set,base_value  

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM