逐位运算一元〜(反转)

时间:2011-09-02 02:44:23

标签: python bit-manipulation

我对~运算符感到有些困惑。代码如下:

a = 1
~a  #-2
b = 15
~b  #-16

~如何运作?

我想,~a会是这样的:

0001 = a
1110 = ~a 

为什么不呢?

6 个答案:

答案 0 :(得分:48)

你是完全正确的。它是two's complement整数表示的工件。

在16位中,1表示为0000 0000 0000 0001。倒置,你得到1111 1111 1111 1110,即-2。同样,15是0000 0000 0000 1111。倒置,你得到1111 1111 1111 0000,即-16。

一般来说,~n = -n - 1

答案 1 :(得分:28)

'〜'运算符定义为: " x的逐位反转定义为 - (x + 1)。它仅适用于整数。" Python Doc - 5.5

这句话的重要部分是这与“整数”有关。 (也称为整数)。您的示例代表一个4位数字。

'0001' = 1 

4位数的整数范围是' -8..0..7'。另一方面,您可以使用未签名的整数',不包括负数,4位数的范围将为' 0..15'。

由于Python对整数进行操作,因此您所描述的行为是预期的。整数用两个补码表示。如果是4位数,则如下所示。

 7 = '0111'
 0 = '0000'
-1 = '1111'
-8 = '1000'

如果您拥有32位操作系统,Python会使用32位进行整数表示。您可以使用以下命令检查最大整数:

sys.maxint # (2^31)-1 for my system

如果您希望为4位数字返回一个无符号整数,则必须进行掩码。

'0001' = a   # unsigned '1' / integer '1'
'1110' = ~a  # unsigned '14' / integer -2

(~a & 0xF) # returns 14

如果你想得到一个无符号的8位数字范围(0..255)而不是使用:

(~a & 0xFF) # returns 254

答案 2 :(得分:6)

看起来我找到了更简单的解决方案,可以满足需要:

uint8: x ^ 0xFF
uint16: x ^ 0xFFFF
uint32: x ^ 0xFFFFFFFF
uint64: x ^ 0xFFFFFFFFFFFFFFFF

答案 3 :(得分:3)

您也可以使用无符号整数(例如来自numpy包)来实现预期的行为。

>>> import numpy as np
>>> bin( ~ np.uint8(1))
'0b11111110'

答案 4 :(得分:0)

Python的一元求反运算符〜x =-(x + 1),该 与翻转内存中的每一位相同:

例如

>>> 0b110    # an integer defined with a binary literal
# 0|1,1,0    = in sign|magnitude form
# +|4,2,0    = each bit's contribution to the int
# +1*(4+2+0) =>
6            

>>> bin(~0b110) # get the binary representation of inverted 0b110
# 1|001         = each bit simply inverted (invert sign bit too)
# -|4+2+0 +1    = each bit's contribution to the int, ‡See note
# -1*(4+2+0+1)  = -7    (the answer we want that represents each bit flipped)
# -0b111        = binary representation of -7
-0b111          = it resembles 1|111 but it in memory it is actually 1|001

-0b111 1|001 在内存中。您不应该将-ve二进制数表示形式解释为存储在内存中的内容,这与正二进制数不同。

•注意:二进制数中的负数倒数,因此-ve位的每个位置仅在组成 int 时为0,并且必须在最终结果中加上-1:

# in-memory  = int  (displayed as)
1|11..111    = -1   (-0b1)
1|11..110    = -2   (-0b10)
1|11..101    = -3   (-0b11)
1|11..100    = -4   (-0b100)
# and so on...

答案 5 :(得分:0)

问题在于,应用〜结果表示的数字没有很好地定义,因为它取决于用于表示原始值的位数。例如:

5 = 101
~5 = 010 = 2

5 = 0101
~5 = 1010 = 10

5 = 00101
~5 = 11010 = 26

但是,在所有情况下,〜5的二进制补码都是相同的:

two_complement(~101) = 2^3 - 2 = 6
two_complement(~0101) = 2^4 - 10 = 6
two_complement(~00101) = 2^5 - 26 = 6

并且考虑到二进制补码用于表示负值,因此将〜5视为其补码的负值-6是有意义的。

因此,更正式地说,要获得此结果,我们有:

  1. 翻转零和一(等同于取一个补数)
  2. 取二进制补语
  3. 施加负号

并且如果x是一个n位数字:

~x = - two_complement(one_complement(x)) = - two_complement(2^n - 1 - x) = - (2^n - (2^n - 1 - x)) = - (x + 1)