为什么~True会得出-2的结果?

137
在Python控制台中:
~True

给我:

-2

为什么?有人能用二进制解释一下这个特殊案例吗?


23
因为~1的结果是-2,所以可以尝试运行True == 1 - Grijesh Chauhan
16
准确来说:"True等于1" 是不正确的,但是 "True等于1" 的表达式 True == 1 是正确的。 - Bach
3
你真的认为看到UNARY_INVERT(整个字节码)会对答案有所帮助吗? - Wooble
2
这个问题不是重复的!它询问了bool的一个具体行为,而不是关于~如何工作的问题。事实上,对这个问题的一个有效回答可以避免提到二进制补码以及~如何操作整数。 - Bakuriu
3个回答

244

int(True)1

1 是:

00000001

~1 是:

11111110

二进制补码1中,-2的表示为:

1先将所有位取反,然后将结果加1,并把结果解释为数字的二进制表示,最后加上负号(因为数字以1开头)。

11111110 → 00000001 → 00000010 
         ↑          ↑ 
       Flip       Add 1

这是2,但符号为负,因为MSB为1。


值得一提的是:

考虑一下bool,你会发现它在本质上是数值类型 - 它只有两个值,TrueFalse,它们只是定制化版本的整数1和0,只是以不同的方式输出。它们是整数类型int子类

所以它们的行为与1和0完全相同,只是bool重新定义了strrepr以便以不同的方式显示它们。

>>> type(True)
<class 'bool'>
>>> isinstance(True, int)
True

>>> True == 1
True
>>> True is 1  # they're still different objects
False

1
@ofcapl еҸӘжғіиҜҙпјҡиҷҪ然 int('1') д№ҹжҳҜ 1пјҢдҪҶ ~'1' еҚҙдјҡдә§з”ҹзұ»еһӢй”ҷиҜҜејӮеёёпјҢиҖҢ ~True еҲҷдёҚдјҡпјҢиҝҷжҳҜеӣ дёә bool жҳҜ int зҡ„еӯҗзұ»гҖӮ@Martijn еңЁд»–зҡ„зӯ”жЎҲдёӯж·»еҠ дәҶиҝҷдёӘдҝЎжҒҜгҖӮ - Grijesh Chauhan
仅供参考,@ofcapl,这个答案展示了二进制算术解释正在发生的事情,而不是实际的字节码(它将是从源代码编译出来的某种中间或操作级别的代码)。 - Patrick M
为什么True在Python中等于1而不是-1,就像其他几乎所有的语言一样?(对不起,PEP索引目前无法使用) - Flávio Etrusco
5
@etrusco,你说的是哪些语言?我完全不知道任何一种语言中 True == -1,但我知道有很多语言中可以说 True == 1... - l4mpi
1
@etrusco @l4mpi 一些老式的BASIC使用-1表示TRUE;它有一个不错的特性,即按位AND和OR运算符同样适用于逻辑AND和OR(在C语言中,只要你不关心短路,x & -1是非零的,在相同情况下x && 1也是非零的)。然而,据我所知,没有主流语言曾经使用过-1来表示TRUE。 - Quuxplusone
1
正式逻辑将“真”定义为单值;所有不是“真”的都是“假”。我所知道的所有编程语言都颠覆了正式逻辑,将“假”定义为单值(0),而所有不是假的都是“真”。例如C#,尽管JavaScript有多种真实性和多种虚假性 - Nicholas Carey

45

Python中的bool类型是int的子类(由于历史原因;布尔值仅在Python 2.3中添加)。

由于int(True)等于1,所以~True等于~1,即-2

请参阅PEP 285了解为什么boolint的子类。

如果你想要布尔值的逆运算,请使用not

>>> not True
False
>>> not False
True
如果您想知道为什么~1-2,那是因为您正在反转有符号整数中的所有位;00000001变成11111110,在一个有符号整数中是一个负数,请参见二进制补码
>>> # Python 3
...
>>> import struct
>>> format(struct.pack('b', 1)[0], '08b')
'00000001'
>>> format(struct.pack('b', ~1)[0], '08b')
'11111110'

初始的1位表示该值为负数,其余的位编码为正数减一的倒数。


1
@GrijeshChauhan:对于二进制补码,您可以使用struct.pack,因为bin(integer)format(integer,'08b')不考虑有符号整数。 - Martijn Pieters
@thefourtheye,MartijnPieters 我尝试了,但很困惑,例如 bin(~True)bin(-2)bin(~1) 都会得到 '-0b10'。如果 -2 的表示是 10,那么为什么要加上 - 符号呢? - Grijesh Chauhan
我的意思是,如果将10本身转换为二进制补码,那么它就是负数了吗? - Grijesh Chauhan
1
@GrijeshChauhan 您可以通过以下方式获取负数和正数的二进制补码表示:format(-2 % (1 << 32), "032b") - thefourtheye
2
我会使用位掩码:format(-2 & ((1 << 32) - 1), "032b") - Martijn Pieters
显示剩余4条评论

4
~True == -2 的结果并不奇怪,如果 True 表示 1~ 表示按位取反的话...前提是

修改如下:

  • 修正了整数表示和按位取反操作符之间的混淆;
  • 进行了其他修饰(消息越短,需要的工作就越多)。

2
~ 不意味着 "2的补码"。~ 意味着 "按位取反"。 - McKay
1
短语“补码”并不是指一种操作,而是指一种在位中存储整数的系统。这种系统实际上并没有在计算机系统中使用。 - McKay

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接