我计算了数组的总和以及同一数组的零填充版本:
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指出舍入问题可能是原因。我不相信,因为添加零不应该改变浮点数(问题出现时,调查那个大小的数据集 - 偏差明显更大)。
答案 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 8或when 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) >= 8
,pairwise 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
使用)。我相信这些算法会
在您提出的零填充问题下产生稳定的结果,但我不够专业,无法肯定地说。