Python 按位取反不翻转位

Python Bitwise Inverse Not Flipping Bits

当使用 pythons 反转 ~ 时,这些位似乎没有像我期望的那样翻转。我相信困惑在于我对 python 如何使用 2 的补码存储数字的理解。

myInt = 5 #101
inverse = ~myInt
print(bin(inverse))

输出:-0b110

预期:-0b010,或 -0b10

这与 two's complement 编码负数的方式有关。任何整数的负数表示都是通过反转数字并加一来计算的。

如果你颠倒那个逻辑,反转二进制表示将取反值并减一。所以 5 的倒数确实应该是 -6。

~5                                                                                                                                                                                                                                  
# -6

由于Python没有为每个整数使用固定位数,因此无法显示所有前导零(有无限个)。所以前面有负号,-0b110 for -6.

随便选一个固定的位数,就可以写出不带负数的二进制数。例如,对于 8 位(一个字节),它将是 1111 1010,这与您预期的相反。

由于 Python 具有任意大小的带符号整数,从概念上讲,它们被符号扩展到无穷大。当我们反转位 0b101 时,我们确实得到 0b010,但符号扩展也被翻转:数字以 1s 的无穷大而不是 0s 开始.这个 1111111....1010 值等于 -6,而不是 -2 - 因为全 1 值将是 -1,然后我们去掉第 4 位和第 1 位。

一般来说,Python 整数的 ~x 等于 -x - 1(等价于 -(x+1))。这对 Python 代码很少有用的操作,但你永远不知道 :)

你是对的 - 这种行为确实与 Python 如何在 twos-complement 中存储负数有关。在幕后,否定一个数字,将一个(有效的)无限的前导列表附加到二进制表示中。因此,虽然 bin(6)0b110,但 bin(-6) 实际上是 0b010(您可以通过观察 bin(-6 & 7) = 0b010 看到这一点。

这意味着使用 ~ 可以做两件事;它翻转二进制表示的第一位,然后再次翻转所有位。

这不是 ~ 运算符的问题,它会执行它应该做的事情,而是您用来显示结果的 bin 函数的问题。

在 Python 中,与在大多数计算机系统中一样,负整数在内部以 "two's complement" 二进制表示形式存储。这意味着 -1 由所有 1 位的序列表示,并且每个较低的整数通过正常的整数减法规则修改该值。所以 -2 是由 -1 减去 1 得到的,你会得到一堆 1 位,最后一位是零。

这里有一些数字和它们的 4 位补码二进制表示:

 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

与许多其他语言不同,Python 的整数没有预定义的位长。它们不是 16 位或 32 位长,就像 C 中的 shortlong 整数一样。相反,它们是动态调整大小的,根据需要添加更多位来表示越来越大的数字.当您需要将二进制数字表示为文本时(如 bin 函数所做的那样),这会导致棘手的情况。如果您知道您的号码仅使用 16 位,则每次都可以写出一个 16 位数字的字符串,但动态大小的号码需要不同的解决方案。

确实,Python 在 bin 函数中做了一些不同的事情。正数用表示其值所需的最短位数写入。负数不是用二进制补码写的(它们实际上在内部编码的方式),而是在其绝对值的位表示前加上一个负号。

所以你得到一个像这样的table,其中按位补码不明显:

 0 :    0b0
 1 :    0b1
 2 :   0b10
 5 :  0b101
-1 :   -0b1
-2 :  -0b10
-3 :  -0b11
-6 : -0b110

至于如何像第一个 table 那样获得负数的二进制表示,唯一好的方法是选择大于任何数字的 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'))