【问题标题】:numpy.sum() giving strange results on large arraysnumpy.sum() 在大型数组上给出奇怪的结果
【发布时间】:2017-06-02 00:36:44
【问题描述】:

我似乎发现了在 numpy 数组上使用 .sum() 的陷阱,但我找不到解释。本质上,如果我尝试对一个大数组求和,那么我会开始得到无意义的答案,但这会默默地发生,而且我无法很好地理解输出以谷歌搜索原因。

例如,这完全符合预期:

a = sum(xrange(2000)) 
print('a is {}'.format(a))

b = np.arange(2000).sum()
print('b is {}'.format(b))

为两者提供相同的输出:

a is 1999000
b is 1999000

但是,这不起作用:

c = sum(xrange(200000)) 
print('c is {}'.format(c))

d = np.arange(200000).sum()
print('d is {}'.format(d))

给出以下输出:

c is 19999900000
d is -1474936480

在更大的阵列上,有可能得到肯定的结果。这更加阴险,因为我可能根本没有发现发生了一些不寻常的事情。例如:

e = sum(xrange(100000000))
print('e is {}'.format(e))

f = np.arange(100000000).sum()
print('f is {}'.format(f))

给出这个:

e is 4999999950000000
f is 887459712

这与数据类型有关,实际上即使使用 python float 似乎也可以解决问题:

e = sum(xrange(100000000))
print('e is {}'.format(e))

f = np.arange(100000000, dtype=float).sum()
print('f is {}'.format(f))

给予:

e is 4999999950000000
f is 4.99999995e+15

我没有 Comp 的背景。科学。并发现自己陷入困境(也许这是一个骗局)。我尝试过的事情:

  1. numpy 数组具有固定大小。没有; this 似乎表明我应该先点击MemoryError
  2. 我可能以某种方式安装了 32 位(可能不相关);不,我关注了this 并确认我有 64 位。
  3. 其他奇怪的sum 行为示例;不()我找到了this,但我看不出它是如何应用的。

有人可以简要解释一下我缺少什么并告诉我需要阅读什么吗?此外,除了记住每次都定义一个dtype 之外,有没有办法阻止这种情况发生或发出警告?

可能相关:

Windows 7

numpy1.11.3

在 Python 2.7.9 上用完 Enthought Canopy

【问题讨论】:

  • 可能是因为numpy 整数依赖于 C 类型的整数,而 python 具有无限的整数范围。花车是……花车。它们可能非常高。
  • 检查np.arange(5).dtype。它可能使用 32 位整数而不是 64 位。此外,请确保您在同一个 Python 安装上执行所有这些检查。
  • 看起来像是某种溢出...整数的符号似乎也被覆盖了,这可能是您有时会得到负面结果的原因。
  • @user2357112 确实在打印dtype('int32')

标签: python python-2.7 numpy


【解决方案1】:

在 Windows 上(在 64 位系统上也是如此),如果您从 Python 整数转换,NumPy 使用的默认整数是 32 位的。在 Linux 和 Mac 上它是 64 位的。

指定一个 64 位整数,它将起作用:

d = np.arange(200000, dtype=np.int64).sum()
print('d is {}'.format(d))

输出:

c is 19999900000
d is 19999900000

虽然不是最优雅,但您可以使用monkey patching 做一些functools.partial

from functools import partial

np.arange = partial(np.arange, dtype=np.int64)

从现在开始,np.arange 默认使用 64 位整数。

【讨论】:

  • 这对我来说很有意义,但我的一个关键问题是是否有办法标记这一点。在调试时,这太容易引起我的注意了;有没有办法确定我错过了这个?
  • 添加了一个可能的解决方案。
  • 另见stackoverflow.com/questions/36278590/…,默认使用C long int,Windows将其定义为32位整数,即使在64位CPU上也是如此。
【解决方案2】:

这显然是 numpy 溢出 32 位的整数类型。通常,您可以使用np.seterr 将 numpy 配置为在这种情况下失败:

>>> import numpy as np
>>> np.seterr(over='raise')
{'divide': 'warn', 'invalid': 'warn', 'over': 'warn', 'under': 'ignore'}
>>> np.int8(127) + np.int8(2)
FloatingPointError: overflow encountered in byte_scalars

但是,sum 明确记录了行为“溢出时不​​会引发错误”,因此您可能在这里运气不佳。使用 numpy 通常是为了方便而牺牲性能!

但是,您可以手动指定累加器的 dtype,如下所示:

>>> a = np.ones(129)
>>> a.sum(dtype=np.int8)  # will overflow
-127
>>> a.sum(dtype=np.int64)  # no overflow
129

观看票 #593,因为这是一个未解决的问题,可能会在某个时候由 numpy 开发人员修复。

【讨论】:

  • 非常感谢您提供门票。我怀疑这是溢出,但没有任何警告让我质疑到底发生了什么。再加上不知道 Windows 会自动将 int 转换为 32 位,这让我感到困惑。
【解决方案3】:

我不是 numpy 专家,但可以用纯 Python 重现您的 arange(200000) 结果:

>>> s = 0
>>> for i in range(200000):
...     s += i
...     s &= 0xffffffff
>>> s
2820030816
>>> s.bit_length()
32
>>> s - 2**32  # adjust for that "the sign bit" is set
-1474936480

换句话说,如果numpy 对有符号 2 的补码 32 位整数进行算术运算,您看到的结果就是我所期望的。

由于我不是numpy 专家,因此我无法提出一个永不感到惊讶的好方法(我本来可以将其作为评论留下,但那时我无法显示格式良好的代码)。

【讨论】:

  • 从搜索“补码”开始。对于各种固定位宽(8、16、32、64),这几乎是所有计算机硬件用来表示有符号整数的方案。在像C 这样的低级语言(CPython 和 numpy 是在其中编码的)中,你可以使用像这样的原生硬件类型。实现 Python 的无限宽二进制补码整数的错觉需要大量代码。
  • 非常感谢,我正在阅读它。那个“大量的幕后代码”保护了我很多,谢谢:) 在这种情况下,Wim 在他的回答中链接到了一张带有numpy 的公开票;众所周知,在这种特殊情况下没有溢出错误(这就是让我感到震惊的原因),但它已经开放多年了:(
【解决方案4】:

Numpy 的默认整数类型与 C long 类型相同。现在,这不能保证在 64 位平台上是 64 位的。事实上,在 Windows 上,long总是 32 位。

结果,numpy sum 溢出值并循环回来。

不幸的是,据我所知,没有方法可以更改默认的dtype。您必须每次都将其指定为 np.int64

您可以尝试创建自己的arange

def arange(*args, **kw):
    return np.arange(dtype=np.int64, *args, **kw)

然后使用那个版本而不是 numpy 的。

编辑: 如果你想标记这个,你可以在你的代码顶部放这样的东西:

assert np.array(0).dtype.name != 'int32', 'This needs to be run with 64-bit integers!'

【讨论】:

    猜你喜欢
    • 2021-12-30
    • 2015-09-16
    • 2015-03-09
    • 1970-01-01
    • 2023-01-27
    • 2013-08-20
    • 2019-12-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多