当添加零时,numpy.sum的行为很奇怪

24

我明白在数值计算中,等价的数学运算可能由于数字误差而导致不同的结果(例如以不同的顺序对浮点数求和)。

然而,令我惊讶的是将零添加到sum中可以改变结果。 我认为这适用于浮点数,无论如何:x + 0. == x

以下是一个例子。我期望所有的行都是完全为零。 有人能否解释为什么会发生这种情况?

M = 4  # number of random values
Z = 4  # number of additional zeros
for i in range(20):
    a = np.random.rand(M)
    b = np.zeros(M+Z)
    b[:M] = a
    print a.sum() - b.sum()

-4.4408920985e-16
0.0
0.0
0.0
4.4408920985e-16
0.0
-4.4408920985e-16
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
2.22044604925e-16
0.0
4.4408920985e-16
4.4408920985e-16
0.0

对于较小的MZ的值,这似乎不会发生。

我还确保a.dtype==b.dtype

这里有另一个例子,它也演示了Python内置的sum的预期行为:

a = np.array([0.1,      1.0/3,      1.0/7,      1.0/13, 1.0/23])
b = np.array([0.1, 0.0, 1.0/3, 0.0, 1.0/7, 0.0, 1.0/13, 1.0/23])
print a.sum() - b.sum()
=> -1.11022302463e-16
print sum(a) - sum(b)
=> 0.0

我正在使用numpy V1.9.2。


5
我可以在1.9.2版本中复现,但在1.6.1版本中无法复现。我猜测是由于较长的数组导致元素以不同的顺序添加,例如为了方便SMID操作。 - amaurea
1
同时使用math.fsum()sum(),它们是不同的函数,至少在2.7.x版本中似乎不会发生这种情况。 - Armin Rigo
3
我猜测这是成对求和,https://github.com/numpy/numpy/pull/3685 - ev-br
无法在Python 2.7.6和NumPy 1.8.0中重现。 - Noel Segura Meraz
同时在 Python 2.7.5 和 NumPy 1.6.2 上也无法复现。 - skippy
1个回答

10

简短回答:你正在看到两者之间的差异。

a + b + c + d

(a + b) + (c + d)

由于浮点数不准确,因此结果不相同。

长答案:Numpy实现了成对求和作为速度优化(它允许更容易地进行矢量化)和舍入误差的优化。

Numpy的sum实现可以在这里找到(函数pairwise_sum_@TYPE@)。它基本上执行以下操作:

  1. 如果数组的长度小于8,则执行常规的for循环求和。这就是为什么在您的情况下,如果W < 4,将不会观察到奇怪的结果 - 在两种情况下都将使用相同的for循环求和。
  2. 如果长度在8到128之间,则将和累积在8个bin r[0]-r[7]中,然后通过((r[0] + r[1]) + (r[2] + r[3])) + ((r[4] + r[5]) + (r[6] + r[7]))求和。
  3. 否则,递归地对数组的两半求和。

因此,在第一种情况下,您得到a.sum() = a[0] + a[1] + a[2] + a[3],在第二种情况下b.sum() = (a[0] + a[1]) + (a[2] + a[3]),这导致a.sum() - b.sum() != 0


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接