Python中"按位非"不考虑二's补码
我需要在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)
第一部分只是建立了一个翻转表,用来把字符串中的 1
和 0
互换。
第二部分获取数字的二进制表示(比如 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()
的答案。)