如何修改整数中的位?

42

我有一个值为70b00000111)的整数,现在想要用一个函数将其替换成130b00001101)。请问在一个整数中替换位的最佳算法是什么?

例如:

set_bits(somevalue, 3, 1) # What makes the 3rd bit to 1 in somevalue?

1
就像在C语言中一样。http://wiki.python.org/moin/BitwiseOperators - Kos
3
注意整数字面值的前缀... 0x十六进制 数字的前缀。你需要的前缀是 0b - Some programmer dude
5个回答

71

这些方法适用于任何大小的整数,甚至大于32位:

def set_bit(value, bit):
    return value | (1<<bit)

def clear_bit(value, bit):
    return value & ~(1<<bit)

如果你喜欢简洁的语言,可以这样写:

>>> val = 0b111
>>> val |= (1<<3)
>>> '{:b}'.format(val)
'1111'
>>> val &=~ (1<<1)
'1101'

为什么不直接使用“value - (1 << bit)”来清除位? - bikram
3
幂等性 - 如果给定的位已经被清除,减法将影响其他位; 位运算根本不会触及其他位。 - Kos

51

你只需要:

def set_bit(v, index, x):
  """Set the index:th bit of v to 1 if x is truthy, else to 0, and return the new value."""
  mask = 1 << index   # Compute mask, an integer with just bit 'index' set.
  v &= ~mask          # Clear the bit indicated by the mask (if x is False)
  if x:
    v |= mask         # If x was True, set the bit indicated by the mask.
  return v            # Return the result, we're done.

>>> set_bit(7, 3, 1)
15
>>> set_bit(set_bit(7, 1, 0), 3, 1)
13

请注意,位数编号(index)从0开始,其中0是最低有效位。

还要注意,新值是被返回的,没有办法像您展示的那样就地修改整数(至少我认为不行)。


2
他随意地说道,仿佛成千上万不熟悉二进制的人在接下来的几年里不需要逐步走过这段代码,才能弄清楚它到底是如何完美运行的。 - temporary_user_name
1
@Aerovistae 呵呵...不确定你是在开玩笑。我添加了注释,使代码更加清晰。希望这能为所有人节省一些工作。 :) - unwind
谢谢!我确实是在开玩笑。不得不完全重新学习我的二进制操作才能理解发生了什么;自从大学以来就没有碰过它们。 - temporary_user_name
你可能会很高兴地知道,这对于numpy数组也同样适用。v可以是一个numpy数组,而index可以是标量或与v长度相同的numpy数组。非常有用!谁知道设置位需要如此聪明。 - John Lunzer
@SwiftsNamesake 我相信它可以被聪明地解决,但这使得目标非常清晰。 - unwind
@SwiftsNamesake,你能否发布一个没有分支的 def set_bit(v, index, x) 实现方案吗? - Kos

11

您可以使用位运算符。

http://wiki.python.org/moin/BitwiseOperators

如果您想将给定的位设置为1,可以在给定位置上使用按位“或”运算符与1:

0b00000111 | 0b00001000 = 0b00001111

如果您想将给定的位设置为0,则可以使用按位“与”运算符:

0b00001111 & 0b11111011 = 0b00001011

请注意,0b前缀表示二进制数,0x前缀表示十六进制数。


嗨,不要发表新答案并删除旧答案,考虑仅编辑您的旧答案。 :-) - sloth
这是我的本意,但我在两个标签页中打开了它,并从错误的标签页发送了它 :) - wmiel
但我想按索引设置字节。 - Váradi Norbert
然后(就像@unwind向您展示的那样),您可以取0b1(= 1)并将其左移到正确的位置(在他的代码中为1 << index)。然后,您可以使用|,&或计算反转,将所有零更改为一。 - wmiel

0

根据提供的示例,看起来您想要交换整数中的位。

例如,在7 (0b00000111)中,如果您交换第3和第1个位置的位,则会得到13 (0b00001101)

我将以下内容作为函数签名swap_bits(val, i, j)

什么是最好的算法? 好吧,以下算法需要恒定时间,O(1)。

def swap_bits(val, i, j):
    """
    Given an integer val, swap bits in positions i and j if they differ
    by flipping their values, i.e, select the bits to flip with a mask.
    Since v ^ 1 = 0 when v = 1 and 1 when v = 0, perform the flip using an XOR.
    """
    if not (val >> i) & 1 == (val >> j) & 1:
        mask = (1 << i) | (1 << j)
        val ^= mask
    return val

例子:

 >>> swap_bits(7, 3, 1)
 13

这段代码利用了位操作技巧,这里有 Sean Anderson 的一个好资源。我正在努力在这里提供 Python 代码片段。


0
这是对@unwind答案的修改,避免使用"if"句子。
def set_bit(v, index, x):
  """Set the index:th bit of v to 1 if x is truthy, else to 0, and return the new value."""
  mask = 1 << index   # Compute mask, an integer with just bit 'index' set.
  v |= mask           # Set the bit indicated by the mask to True.
  v ^= (not x) * mask # If x is True, do nothing (XOR with 0). If x is False, use the mask for clearing the bit indicated by the mask (XOR with 1 in the requested position).
  return v            # Return the result, we're done.

>>> set_bit(7, 3, 1)
15
>>> set_bit(set_bit(7, 1, 0), 3, 1)
13

请注意,位数(索引)从0开始,其中0表示最低有效位。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接