为什么-0.0和0.0不相同?

7

我可能会忽略一些基本的东西,但请考虑以下这个解释器的会话:

>>> -0.0 is 0.0
False
>>> 0.0 is 0.0
True
>>> -0.0  # The sign is even retained in the output.  Why?
-0.0
>>>

你或许会认为 Python 解释器会意识到 -0.00.0 是同一个数字。实际上,它将它们视为相等:

>>> -0.0 == 0.0
True
>>>

那么为什么Python要区分这两种情况并生成一个全新的对象来表示“-0.0”?对于整数,它并不会这样做:
>>> -0 is 0
True
>>> -0  # Sign is not retained
0
>>>

现在,我意识到浮点数是计算机中的一个巨大问题,但这些问题都与它们的准确性有关。例如:

>>> 1.3 + 0.1
1.4000000000000001
>>>

但这不是一个精度问题,对吧? 我的意思是,我们在谈论数字的符号,而不是它的小数位。


1我可以在Python 2.7和Python 3.4中重现此行为,因此这不是一个特定于版本的问题。


1
我认为这是IEEE 754浮点表示法的一种特性,这意味着它也不特定于Python。 - Fred Larson
整数溢出可能是计算机中更严重的“问题源”。 - tmyklebu
1
为什么你在数字比较中使用 is? 你的问题与有符号零无关:尝试 x = 2.3y = 2.3,然后执行 x is y。 然后,只是为了好玩,尝试 x = 2.3; y = 2.3(一行内),然后执行 x is y - Mark Dickinson
1
针对@MarkDickinson的评论:有一种非常小的文字子集,其中任何您分配给它们的变量都只是指向它们在内存中始终存在的位置的指针。我忘记了推理,但它类似于“基本值集将始终需要”。0、1、2和其他一些整数。其他所有内容都会被创建。注意:使用x = 2; y = 2x is y,您将看到True。尝试x=-240000; y=-240000然后x is y,您将看到False。希望这可以帮助下一个访问者! - Mike Williamson
3个回答

12
在IEEE754中,浮点数的格式中,符号是一个单独的位。因此,-0.0和0.0在这一位上不同。 整数使用二进制补码来表示负数;这就是为什么只有一个0。 只有当您真正想比较对象的实例时,才使用is。否则,特别是对于数字,请使用==
>>> 1999+1 is 2000
False

>>> 0.0 == -0.0
True

我接受你的答案,因为你解释了浮点数和整数的情况。然而,你能想到任何 -0.0 的实际用例吗?我知道有一些理论上的用途,但它似乎只会妨碍。 - user2555451
7
@iCodez说:有符号零对于实现复杂数学函数非常有用。经典论文是:William Kahan,Branch Cuts for Complex Elementary Functions,or Much Ado About Nothing's Sign Bit。 《The State of the Art in Numerical Analysis》, Clarendon Press, Oxford, 1987。通过Google Scholar很容易找到在线副本。 - njuffa
请注意,Python并没有明确规定IEEE-754。参考文献中指出:“这些表示机器级双精度浮点数。您要依赖底层机器架构(以及C或Java实现)来接受范围和处理溢出。”但在大多数平台和实现中,这意味着IEEE-754双精度浮点数,或者非常接近它。 - abarnert
@njuffa:同样也在Kahan的网站上。 - tmyklebu
1
有趣的 Python 常识:请注意,10 + 1 等于 11 返回 True。前256个值(以及一些负数)被内部化,因此您始终会得到相同的实例。 - Stefano Borini
@StefanoBorini,似乎Python中的某些内容已经发生了变化。我刚刚尝试了一下,对于任何整数,1500000 + 1 is 1500001都会返回True。然而,-0.0 is 0.0仍然返回False。我以为我理解了is,但现在我又感到困惑了。(我在Python 3.7.6上尝试了这个问题。) - Mike Williamson

9

IEEE浮点算术标准(IEEE 754)定义了包括带符号零。从理论上讲,它们允许您区分负数下溢和正数下溢

就Python而言,请使用 == 而不是 is 来比较数字。


4

因为这两个数字的二进制表示不同。在0.0中,第32位是0,在-0.0中,第32位是1。


好的。答案仍然大致相同。浮点数中的最后一位是符号位。0表示正数,1表示负数。 - Mr. Mascaro
1
实际上,在2.x版本中,Python中的浮点数通常是未指定的,并且在CPython中被指定为“用于构建解释器的C编译器中的任何double”。在3.x版本中,他们进行了一些清理,并将它们指定为跨实现的“机器double”,但请注意,“您受制于底层机器架构(以及C或Java实现)”。 - abarnert

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