Python中"按位非"不考虑二's补码

6 投票
2 回答
1441 浏览
提问于 2025-04-20 19:08

我需要在Python中做一个“~”操作,但不想考虑二进制补码。我通过使用异或(XOR)实现了这个操作,你知道还有其他更有效的方法吗?

a = 0b101
b = 0b10101

print bin(a ^ (2 ** a.bit_length() - 1)) #0b10
print bin(b ^ (2 ** b.bit_length() - 1)) #0b1010

2 个回答

1

还有一种方法,虽然有些人(包括我自己)可能会争论这方法是否更好:

tbl = str.maketrans("01","10")

int(bin(42)[2:].translate(tbl),2)

第一部分只是建立了一个翻转表,用来把字符串中的 10 互换。

第二部分获取数字的二进制表示(比如 42 变成 0b101010),然后去掉前面的 0b,接着通过翻转表来翻转这些位。最后,你只需用 int(,2) 将这个二进制字符串转换回数字。


如果你能把宽度限制在特定的范围,而不是使用数字本身的宽度,那么这就变得简单了(以32位为例):

val = val ^ 0xffff
5

这就是~的作用。比较复杂的是,Python里的整数可以无限大,所以当你对一个数字取反时,理论上会在数字前面加上无数个1。这就意味着你会得到负数。

>>> bin(~0b101)
'-0b110'
>>> bin(~0b10101)
'-0b10110'

要把这些负数转换成无符号数,你需要决定你关心多少位数。比如说你在处理8位的字节,那你可以用一个字节的1来进行与运算:

>>> bin(~0b101 & 0xFF)
'0b11111010'
>>> bin(~0b10101 & 0xFF)
'0b11101010'

或者如果你想要和输入数字的位数完全匹配,你的解决方案也是合理的。为了提高效率,你可以用左移代替指数运算。而且用~&可能会比用^更清晰。

>>> bin(~a & ((1 << a.bit_length()) - 1))
'0b10'
>>> bin(~b & ((1 << b.bit_length()) - 1))
'0b1010'

(我觉得像& 0xFFFF这样的硬编码掩码在实际中可能是个好解决方案。我想不出一个好的实际应用场景来使用bit_length()的答案。)

撰写回答