使用Python的取反运算符~时,似乎比我预期的没有将位翻转。我相信混淆在于我的理解Python使用2的补码存储数字的方式。
myInt = 5 #101
inverse = ~myInt
print(bin(inverse))
输出结果:-0b110
期望结果:-0b010,或者-0b10
~
有关的问题,它执行的是应该执行的操作。相反,这是与你使用来显示结果的bin
函数有关的问题。-1
由所有1
位的序列表示,并且每个较低的整数都通过常规整数减法规则修改该值。因此,-2
通过从-1
中减去1
而形成,您会得到一堆1
位,后面跟着一个零作为最后一位。 0 : 0000
1 : 0001
2 : 0010
5 : 0101
-1 : 1111 # this is ~0
-2 : 1110 # this is ~1
-3 : 1101 # this is ~2
-6 : 1010 # this is ~5
short
和long
整数那样是16位或32位长。相反,它们是动态大小的,需要更多的位来表示越来越大的数字。当您需要将二进制数字表示为文本(如bin
函数所做的)时,这会导致棘手的情况。如果您知道您的数字仅使用16位,则每次都可以写出一个16位数字字符串,但动态大小的数字需要不同的解决方案。bin
函数中,Python采用了一种不同的方法。正数以表示其值所需最少位数的方式进行编写。而负数则不是采用二进制补码(它们实际上是如何在内部编码的),而是在其绝对值的位表示前面放置一个减号。 0 : 0b0
1 : 0b1
2 : 0b10
5 : 0b101
-1 : -0b1
-2 : -0b10
-3 : -0b11
-6 : -0b110
对于如何获取类似第一张表中的负数的二进制表示,唯一好的方法是选择某个大于所有数字的2的幂次方,并在格式化之前将其添加到所有负值中:
MY_MAXINT = 2**4
for v in [0,1,2,5,-1,-2,-3,-6]:
if v < 0:
v += MY_MAXINT
print(format(v, '04b'))
~5
# -6
-0b110
表示-6
。1111 1010
,这是您预期的反向数。由于Python具有任意大小的带符号整数,从概念上讲,它们被无限地扩展。当我们反转位0b101
时,确实得到0b010
,但是符号扩展也会翻转:数字以无限数量的1
开头,而不是0
。这个值1111111....1010
等于-6,而不是-2,因为全1的值将是-1,然后我们减去4和1位。
一般来说,对于Python整数,~x
等于-x - 1
(或者等效地,-(x+1)
)。这在Python代码中很少使用,但是你永远不知道:)
你说得对 - 这种行为确实与Python如何在二进制补码中存储负数有关。在幕后,取反一个数字相当于在二进制表示中添加一个(实际上)无限的前导1列表。因此,虽然bin(6)
是0b110
,但bin(-6)
实际上是0b010
(您可以通过观察bin(-6 & 7) = 0b010
来看到这一点)。
这意味着使用~
会做两件事情;它翻转二进制表示的第一位,然后再次翻转所有位。