如何在Python中执行位取反操作?

50
为了测试使用更基本的构建模块(在我的案例中使用Nand、Or和And)构建Xor操作,我需要能够执行Not操作。内置的not似乎只能处理单个位。如果我执行以下操作:
x = 0b1100
x = not x

我应该得到0b0011,但实际上我只得到了0b0。我做错了什么?还是Python缺乏这种基本功能?

我知道Python有一个内置的异或函数,但我一直在使用Python测试HDL项目/课程中需要构建异或门的东西。我想在Python中测试这个,但没有等效于Not门的功能。


1
你需要小心。not 不是一个位运算符。这些是位运算符(实际上它们映射到门):https://wiki.python.org/moin/BitwiseOperators - rady
实际上,你得到的不是 0b0,而是 False 单例对象,因为 not 始终返回其操作数的相反值(无论是什么),这是由其真实性确定的。 - Bachsau
7个回答

59

在 Python 中使用 ~ 的问题是它只适用于有符号整数。除非限制位数,否则这也是唯一有意义的方式。它可以用于按位运算,但可能会使解释中间结果变得困难。

对于 4 位逻辑,您应该从 0b1111 中减去所需值。

0b1111 - 0b1100  # == 0b0011

对于8位逻辑,从0b11111111中减去等等。

通用形式为:

def bit_not(n, numbits=8):
    return (1 << numbits) - 1 - n

谢谢!如果Python允许无符号数字会很好,但那将是一个巨大的改变,所以这个也可以。 :) - Lauren
1
请注意,您也可以构建一个掩码并进行AND运算(Python的伪二进制补码系统通过将其约束为由掩码限制的正数来正确处理它),例如 return ~n & ((1 << numbits) - 1)。您的解决方案很好,但对我来说感觉有点反向,因为我更喜欢按实际翻转位,然后掩盖到适当的宽度。 - ShadowRanger
约翰的方法更快 - Nicolas David
我不明白为什么Python强大的开发者们不在“operator”模块中添加“xor”。我们有按位“and”和“or”,那么为什么没有“xor”和“not”呢? - John White

10
另一种实现方法是分配一个掩码,如下所示(应全部为1):

mask = 0b1111

然后像这样将其与您的数字异或:

number = 0b1100
mask = 0b1111
print(bin(number ^ mask))

你可以参考异或运算的真值表来了解它的工作原理。

1
记录一下,这取决于输入已经在该范围内,否则输出将保持在范围之外;你不能用它来进行削减。如果您想要它适用于可能超出所需范围的数字,~number & mask 需要稍微多做一点工作,看起来更接近您打算的逻辑。例如,0x12345 ^ 0xffff 得到 0x1dcba,而 ~0x12345 & 0xffff 得到 0xdcba - ShadowRanger

10

Python位运算符 ~ 反转整数的所有位,但是我们无法看到原生结果,因为Python中的所有整数都具有有符号的表示形式。

间接地,我们可以检查:

>>> a = 65
>>> a ^ ~a
-1

或者同样的:

>>> a + ~a
-1

结果为-1表示所有位都被设置。但负号不允许我们直接检查这个事实:

>>> bin(-1)
'-0b1'

解决方法很简单:我们必须使用无符号整数。第一种方式是导入numpyctypes模块,两者都支持无符号整数。但是对于我来说,numpy比ctypes更简单易用:
import numpy as np
a = np.uint8(0b1100)
y = ~x

检查结果:

>>> bin(x)
'0b1100'
>>> bin(y)
'0b11110011'

最后进行检查:

>>> x + y
255

对于8位整数(字节),无符号整数'255'与有符号整数'-1'的含义相同,因为它们所有的位都被设置为1。请确保:

>>> np.uint8(-1)
255

还有一种最简单的解决方案,虽然不是非常正确,但如果你想要包含额外的模块,你可以使用异或运算来反转所有位,其中第二个参数的所有位都设置为1:

a = 0b1100
b = a ^ 0xFF

执行此操作还将删除有符号整数的最高有效位,我们可以看到以下结果:

>>> print('{:>08b}'.format(a))
00001100
>>> print('{:>08b}'.format(b))
11110011

最终的解决方案包含了一项额外的操作,因此并不是最优的:

>>> b = ~a & 0xFF
>>> print('{:>08b}'.format(b))
11110011

1

尝试一下,这个叫做按位取反运算符

~0b1100

23
这个答案是错误的,因为Python中的整数是有符号的,对于任何整数i,~i总是等于简单的-i-1 - xdavidliu
1
这通常是在整数上进行位运算时所期望的,不是吗? - Gark Garcia

1
这里的答案都包含了很有价值的信息,但是每个答案都不能很好地处理边缘情况。与其依赖于一个8位掩码或者要求程序员改变掩码中的比特数,不如通过输入创建一个基于 bit_length() 的掩码。
def bit_not(num):
    return num ^ ((1 << num.bit_length()) - 1)

0

二进制字符串可以用来保留左侧的0,因为我们知道:

bin(0b000101) # '0b101'
bin(0b101)    # '0b101'

此函数将返回输入数字的“NOT”字符串格式

def not_bitwise(n):
     ''' 
     n: input string of binary number (positive or negative)
     return: binary number (string format)
     '''
     head, tail = n.split('b')
     not_bin = head+'b'+tail.replace('0','a').replace('1','0').replace('a','1')
     return not_bin

例子:

In[266]: not_bitwise('0b0001101')
Out[266]: '0b1110010'

In[267]: int(not_bitwise('0b0001101'), 2)

Out[267]: 114

In[268]: not_bitwise('-0b1010101')

Out[268]: '-0b0101010'

In[269]: int(not_bitwise('-0b1010101'), 2)
Out[269]: -42

-1

John La Rooy 给出的一般形式可以简化为以下方式(Python == 2.7 and >=3.1):

def bit_not(n):
    return (1 << n.bit_length()) - 1 - n

只有当 n 的逻辑高位设置为 1 时才有效。如果你在逻辑上使用八位数字,那么这将适用于 n 在范围内(128,256),但对于 n 在范围内(128),它将无法设置高位。这也使其不可逆转(因为每次反转时数字将不断缩小,因为应该保留的高位丢失了);即使设置了高位,结果也不会设置它,因此下一个反转就变得错误了。 - ShadowRanger

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