【问题标题】:numpy treatment of zeros: distinguishing -0 from 0零的numpy处理:区分-0和0
【发布时间】:2021-05-06 14:18:45
【问题描述】:

已经注意到 numpy 中关于区分 -0.0 和 0.0 的一些相当特殊的行为。以下是一些示例:

#normal python doesn't distinguish between 0 and -0:
>>> -0
0
>>> -0==0
True

#numpy also sometimes changes -0 to 0:
>>> import numpy as np
>>> np.array([-0])
array([0])

#HERE IS THE SURPRISE - numpy does seem to have separate 0 and -0:
>>> np.round(0.1)
0.0
>>> np.round(-0.1)
-0.0

#yet numpy is of course aware that -0 and 0 are equal:
>>> np.round(-0.1) == np.round(0.1)
True

#python round() function doesn't behave like this:
>>> round(0.1)
0
>>> round(-0.1)
0

背景 - 我为什么要关心这个?因为我有一个 numpy 数组列表,并且我想将与列表中另一个数组相等的数组删除为 2d.p.。为此,我将数组列表更改为数组字典,其中每个数组的键item 是舍入为 2d.p 的数组。现在一个数组不能作为dict key,所以我在四舍五入后使用了.tobytes(),并且四舍五入后的数组的字节表示就是item的key。该项目的值是未舍入的数组,因为我想保持精度。
想象一下,当我注意到这并没有消除相同的数组时,我感到很惊讶,这仅仅是因为一个有一个 -0 而另一个有一个 0...

>>> np.round(0.1).tobytes()           #ends with x00
b'\x00\x00\x00\x00\x00\x00\x00\x00'
>>> np.round(-0.1).tobytes()          #ends with x80
b'\x00\x00\x00\x00\x00\x00\x00\x80'

>>> np.round(0.1).tobytes() == np.round(-0.1).tobytes()  #well obviously this will be False
False

为什么 numpy 以不同的方式存储 -0 和 0,为什么有时而不总是?还有其他这种行为的例子吗 - 以上是我迄今为止提出的。为什么 numpy 在这方面与 python 不同?当python也有单独的0和-0时,有什么例子吗?如何让我的代码识别数组中一个位置的 -0 和 0 是相同的?如果您有 -0.0,则 adding 0 会将其更改为 0,但 subtracting 0 会将其保留为 -0.0。这是为什么呢?

【问题讨论】:

  • 对于第一个问题,这是由于 IEEE-754 标准。数字需要标准化以避免这种情况。对于第二点,我想 oskros 的链接提供了答案。
  • 酷 - 来自 oskros 的链接建议通过向数组添加 0.0 来进行标准化。
  • @oskros 谢谢 - 我没能找到那个帖子,但已经发现自己添加零有帮助。我的问题的主要目的是关于 为什么 这一切都像这样工作的理论方面,而不是如何解决这个问题。事实上,即使我写的答案(加零)也清楚地表明,虽然这有效,但我错过了这一切背后的基本理解和逻辑
  • @JérômeRichard 感谢有关 IEEE-754 的信息。这种行为的解释是什么?为什么标准设置为具有单独的 0 和 -0,当测试相等时返回 True,而在数学上(我假设)没有单独的 0 和 -0?

标签: python numpy


【解决方案1】:

我认为你错过了一个简单但关键的点:

>>> print(0, -0, 0.0, -0.0)
0 0 0.0 -0.0

您以np.array([-0]) 结尾的初始语句都创建整数,因为没有小数点的数字或e 在python 中是整数文字。 Python 并不完全使用正常的二进制补码来表示其无限精度整数,但仍然只有一种方法可以表示零,并且没有方法表示 NaN。

同时,/ 的结果或带有小数点的文字或 e 被解释为 64 位 IEEE-754 浮点数。在这种表示中,有很多方法可以同时表示零和 NaN。

因此,如果您想要负零,请不要使用整数文字:直接使用浮点数。您执行的其他操作(例如舍入)只是将数字转换为浮点数的复杂方法,浮点数可以表示负零。

通过扩展,round 函数的结果也完全符合预期。 Numpy 返回一个float,而python 返回一个int。您可以在命令行上打印的结果的repr 中看到这一点:float zero 看起来像0.0 或0.,而 int zero 只是0。

这一切都归结为一个事实,常见的整数表示不能区分零,而浮点数可以。

【讨论】:

  • 是的,我错过了 - 我意识到这一点,并在您添加此答案时将其作为评论添加到我的问题中。感谢您指出了这一点!为什么整数只有一个零,而浮点数有两个零?似乎不一致 - 以及奇怪的是应该有两个零 - 到底是什么目的......
  • @gnoodle。您必须查看二进制表示以了解原因。这是完全明智的。 IEEE-754 中有多个零和多个​​ NaN。它们存在,所以人们使用它们。
【解决方案2】:

关于问题 2 - 一种解决方法:

将 0 添加到数组中。由于某种原因,这会返回 0.0 而不是 -0.0:

>>> np.round(-0.1)+0
0.0

使用.tobytes() 显然与“正常”零相同:

>>> (np.round(-0.1)+0).tobytes()
b'\x00\x00\x00\x00\x00\x00\x00\x00'
>>> np.round(0.1).tobytes() == (np.round(-0.1)+0).tobytes()
True

所以,在四舍五入到 2d.p 之后。但在使用.tobytes() 之前,将零添加到数组中。

关于问题 1“为什么 numpy 存储 -0 和 0 的方式不同”

见https://en.wikipedia.org/wiki/Signed_zero:

“据称,IEEE 754 中包含有符号零使得它 在某些关键方面更容易实现数值精度 问题,[1] 特别是在使用复杂的初等计算时 [2]另一方面,有符号零运行的概念 与大多数数学领域的一般假设相反 负零与零是一回事。表示 允许负零可能是程序错误的来源,如果软件 开发商没有考虑到,虽然这两个零 表示在数值比较下表现相同,它们产生 某些操作会产生不同的结果。”
参考
[1] 威廉·卡汉(William Kahan),“复杂基本函数的分支切割,或无事生非”,数值分析的最新技术(Iserles 和 Powell 编),Clarendon Press,牛津,1987 年。
[2] William Kahan,复数 z 平面中的导数,p。 10.

【讨论】:

  • 2*(-0) 当然应该是-0。 -1-1==-2 而不是 0 会让你感到惊讶吗?希望这也能澄清-0+0。
  • 虽然很高兴您能抽出时间来玩,但这是问题的一部分,或者是一个全新的问题。请记住,SO 是一个问答网站,而不是一个有话题的论坛。答案应该是独立的。
猜你喜欢
  • 2015-04-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-03
  • 1970-01-01
  • 1970-01-01
  • 2018-05-14
  • 1970-01-01
  • 2016-09-02
相关资源
最近更新 更多