获取和设置值的特定位

Get and Set specific bits of a value

对于给定的值,我需要能够读取和写入该值的特定位。

示例

n = 341
# bin(341) is '0b101010101'

get_bits(n, start = 2, end = 6)
# Returns 10 = '0b1010'

n = set_bits(n, start = 2, end = 6, newValue = 6)
# Replaces bits [2:6] of n with 0b0110
# Makes n = 309 = '0b100110101'

这里有三个函数可以做到这一点! (第一个包含一些常用代码):

def mask_and_shift(start, end, length):
    """Return a mask and shift value to access [<start>:<end>] of <length> bits"""
    shift = length - end
    mask = (1 << end - start) - 1 << shift
    return mask, shift

def get_bits(n, start, end, length : int = None):
    """Read bits [<start>:<end>] of <n> and return them
    <length> forces <n> into that bit length"""
    mask, shift = mask_and_shift(start, end, length = n.bit_length() if length is None else length)
    return (n & mask) >> shift

def set_bits(n, start, end, newValue, length : int = None):
    """Change bits [<start>:<end>] of <n> to <newValue> and return <n>
    <length> forces <n> into that bit length"""
    mask, shift = mask_and_shift(start, end, length = n.bit_length() if length is None else length)
    return (n & ~mask) | (newValue << shift)

(请随意提出改进建议,两者的第一行都非常简陋,但我更喜欢这种语法,而不是将三元运算符分成另一行,因为这样看起来确实应该将其整理成 mask_and shift 尽管这会使 mask_and_shift 本身无用)

通常位从右到左编号。如果您使用该标准约定和开始-停止值(如范围),您的函数将更易于使用按位运算符实现:

def get_bits(n, start, end):
    return (n&((1<<end)-1))>>start

def set_bits(n, start, end, value):
    mask = (1<<end) - (1<<start) 
    return (n&~mask) | (value<<start)&mask

输出:

n = 341  # 101010101
         # 876543210 bit numbers

                           # 101010101 = 341 
f"{get_bits(n,3,7):b}"     # ..1010... = 10
                           # 876543210

                           # 101010101 = 341 
f"{set_bits(n,3,7,12):b}"  # 101100101 = 357
                           # ..^^^^...
                           # ..1100... = 12
                           # 876543210