为什么NumPy在对零填充数组求和时给出不同的结果?

时间:2017-11-09 11:42:01

标签: python numpy wolfram-mathematica

我计算了数组的总和以及同一数组的零填充版本:

import numpy as np

np.random.seed(3635250408)

n0, n1 = int(2**16.9), 2**17

xx = np.random.randn(n0)
yy = np.zeros(n1)
yy[:n0] = xx

sx, sy = np.sum(xx), np.sum(yy)

print(f"sx = {sx}, sy = {sy}") # -> sx = -508.33773983674155, sy = -508.3377398367416
print(f"sy - sx:", sy - sx)  # -> sy - sx: -5.68434188608e-14
print("np.ptp(yy[:n0] - xx) =", np.ptp(yy[:n0] - xx)) # -> 0 

为什么我得不到相同的结果?

有趣的是,我能够在Mathematica中显示出类似的效果。我使用的是Python 3.6(支持MKL的Anaconda 5.0)和Numpy 1.13.3。也许,这可能是一个MKL问题吗?

更新: @ rich-l和@jkim指出舍入问题可能是原因。我不相信,因为添加零不应该改变浮点数(问题出现时,调查那个大小的数据集 - 偏差明显更大)。

2 个答案:

答案 0 :(得分:2)

此时您可能遇到浮点精度问题。

默认情况下,numpy使用双精度浮点数来存储值,精度为16位。第一个结果输出17位数。

我怀疑在前一种情况下,值的波动导致两个值以不同方式舍入略微,前者导致舍入为一半(5.5e-16),并且后者超过阈值四舍五入到完整数(6.0e-16)。

然而,这只是一个假设 - 我不确定numpy如何舍入最低位数。

答案 1 :(得分:1)

浮点算术是not associative

In [129]: ((0.1+0.2)+0.3) == (0.1+(0.2+0.3))
Out[129]: False

因此,添加项目的顺序会影响结果。 numpy.sum通常使用pairwise summation。当数组的长度为less than 8when summing over a strided axis时,它会恢复为天真求和(从左到右)。

由于成对求和递归地将序列分成两组,因此 添加零填充会影响序列被分割的中点,从而影响 改变添加值的顺序。而且自浮点数 算术不是关联的,零填充会影响结果。

例如,考虑

import numpy as np

np.random.seed(3635250408)
n0, n1 = 6, 8
xx = np.random.randn(n0)
# array([ 1.8545852 , -0.30387171, -0.57164897, -0.40679684, -0.8569989 ,
#         0.32546545])

yy = np.zeros(n1)
yy[:n0] = xx
# array([ 1.8545852 , -0.30387171, -0.57164897, -0.40679684, -0.8569989 ,
#         0.32546545,  0.        ,  0.        ])

xx.sum()yy.sum()的值不同:

In [138]: xx.sum()
Out[138]: 0.040734223419930771

In [139]: yy.sum()
Out[139]: 0.040734223419930826

In [148]: xx.sum() == yy.sum()
Out[148]: False

len(xx) < 8开始,xx中的值从左到右相加:

In [151]: xx.sum() == (((((xx[0]+xx[1])+xx[2])+xx[3])+xx[4])+xx[5])
Out[151]: True

由于len(yy) >= 8pairwise summation用于计算yy.sum()

In [147]: yy.sum() == (yy[0]+yy[1]+yy[2]+yy[3])+(yy[4]+yy[5]+yy[6]+yy[7])
Out[147]: True

相关的NumPy开发人员讨论:

numpy.sum不使用Kahan也不使用Shewchuk求和(math.fsum使用)。我相信这些算法会 在您提出的零填充问题下产生稳定的结果,但我不够专业,无法肯定地说。