Python Numpy总和(Numpy 1.15.4,与MKL挂钩)

Python Numpy总和(Numpy 1.15.4,与MKL挂钩),python,numpy,intel-mkl,Python,Numpy,Intel Mkl,我正在运行来自Anaconda的Python/Numpy(1.15.4)的最新版本,该版本与MKL链接。我使用以下ε: epsilon = 2**(-53) 所以1.0+ε等于1.0。然后我定义了下面的numpy数组,除了前8个元素等于1之外,它用epsilon填充 import numpy as np n = 1000000 a = np.full(n, epsilon) a[0:8] = 1.0 如果你用从左到右的经典约化计算数组的和,你应该得到8.0,因为所有的ε都不会随着约化的进行而

我正在运行来自Anaconda的Python/Numpy(1.15.4)的最新版本,该版本与MKL链接。我使用以下ε:

epsilon = 2**(-53)
所以1.0+ε等于1.0。然后我定义了下面的numpy数组,除了前8个元素等于1之外,它用epsilon填充

import numpy as np
n = 1000000
a = np.full(n, epsilon)
a[0:8] = 1.0
如果你用从左到右的经典约化计算数组的和,你应该得到8.0,因为所有的ε都不会随着约化的进行而改变。但我很惊讶地看到这一点

print(np.sum(a))
给你8.00000000011008,这不是我所期望的。我试着把这些放在中间,并在数组的末尾检查总数是否没有倒退,但没有变化。你知道怎么算吗


PS:我很清楚浮点运算很棘手,而且+与浮点无关,这使得归约的结果取决于求和的顺序。但我无法找出这里使用的求和顺序。

如注释中所述,
numpy
使用成对求和。因此,当调用堆栈开始解析时,在递归两两求和结束时,求和(大致)如下所示:

(1+1) + (1+1) + (1+1) + (1+1) + (epsilon + epsilon) + ... + (epsilon + epsilon)
(2+2) + (2+2) + (2*epsilon) + (2*epsilon) + ... + (2*epsilon)
(4+4) + (4*epsilon) + (4*epsilon) + ... + (4*epsilon)
8 + (8*epsilon) + (8*epsilon) + ... + (8*epsilon)
8 + (16*epsilon) + (16*epsilon) + ... + (16*epsilon)
...
8 + (999992*epsilon)
您认为
1.0+epsilon
等于
1.0
是正确的。人们很容易认为,
x+epsilon==x
对于所有
x
。当
x
为“大”时,它确实有效,但当
x==epsilon
时(即
epsilon+epsilon!=epsilon
)它不有效。因此,
epsilon+epsilon
术语将开始堆积:

In [27]: epsilon = 2**(-53)

In [28]: 1.0 + epsilon == 1.0
Out[28]: True

In [29]: 2.0 + epsilon == 2.0
Out[29]: True

In [30]: epsilon + epsilon == epsilon
Out[30]: False

In [31]: epsilon
Out[31]: 1.1102230246251565e-16

In [32]: epsilon + epsilon
Out[32]: 2.220446049250313e-16

In [33]: 123*epsilon
Out[33]: 1.3655743202889425e-14
我不能完全得到numpy的答案,但我们可以非常接近:

In [36]: 8 + (999992*epsilon)
Out[36]: 8.000000000111022

In [62]: def pairwise_sum(arr):
    ...:   if len(arr) <= 2:
    ...:     return sum(arr)
    ...:   midpoint = len(arr)//2
    ...:   first_half = arr[:midpoint]
    ...:   second_half = arr[midpoint:]
    ...:   return pairwise_sum(first_half) + pairwise_sum(second_half)
    ...:

In [63]: pairwise_sum(a)
Out[63]: 8.0000000001110205
[36]中的
8+(99992*epsilon)
Out[36]:8.000000000111022
在[62]中:定义成对求和(arr):

…:如果len(arr)我知道浮点运算很复杂,但是1.0+ε应该等于1.0,8.0+ε也应该等于1,即使使用浮点算术OK,我现在看到你的点了
1+epsilon==1
True
但是设置
epsilon=0
确实会退出
8
,正如回答者随机评论的那样:numpy的总和并没有做简单的求和(但也没有那么昂贵)。但我现在没有联系。编辑啊,它是。@sascha:它非常接近我要找的东西。我想知道Numpy在这里做什么。上面的维基条目说这是Numpy的默认设置,并链接到。但是关于这个话题(当前的工作…)似乎有一些牵引力,所以我不会按照所有这些路径来解释当前的状态/添加答案。