添加零时出现奇怪的 numpy.sum 行为
Weird numpy.sum behavior when adding zeros
我了解数学上等价的算术运算如何由于数值错误(例如,以不同的顺序对浮点数求和)而导致不同的结果。
然而,令我惊讶的是,向 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
M
和 Z
的较小值似乎不会发生。
我也确定了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。
简答:您看到了
之间的区别
a + b + c + d
和
(a + b) + (c + d)
由于浮点数不准确,这并不相同。
长答案:Numpy 实现成对求和作为速度(它允许更容易的矢量化)和舍入误差的优化。
可以找到 numpy 求和实现 here(函数 pairwise_sum_@TYPE@
)。它基本上执行以下操作:
- 如果数组长度小于8,则执行常规的for循环求和。这就是为什么如果
W < 4
在您的情况下没有观察到奇怪的结果 - 在两种情况下都将使用相同的 for 循环求和。
- 如果长度在 8 到 128 之间,它会在 8 个 bin 中累加总和
r[0]-r[7]
然后将它们相加 ((r[0] + r[1]) + (r[2] + r[3])) + ((r[4] + r[5]) + (r[6] + r[7]))
。
- 否则,它递归地对数组的两半求和。
因此,在第一种情况下你得到 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
.
我了解数学上等价的算术运算如何由于数值错误(例如,以不同的顺序对浮点数求和)而导致不同的结果。
然而,令我惊讶的是,向 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
M
和 Z
的较小值似乎不会发生。
我也确定了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。
简答:您看到了
之间的区别a + b + c + d
和
(a + b) + (c + d)
由于浮点数不准确,这并不相同。
长答案:Numpy 实现成对求和作为速度(它允许更容易的矢量化)和舍入误差的优化。
可以找到 numpy 求和实现 here(函数 pairwise_sum_@TYPE@
)。它基本上执行以下操作:
- 如果数组长度小于8,则执行常规的for循环求和。这就是为什么如果
W < 4
在您的情况下没有观察到奇怪的结果 - 在两种情况下都将使用相同的 for 循环求和。 - 如果长度在 8 到 128 之间,它会在 8 个 bin 中累加总和
r[0]-r[7]
然后将它们相加((r[0] + r[1]) + (r[2] + r[3])) + ((r[4] + r[5]) + (r[6] + r[7]))
。 - 否则,它递归地对数组的两半求和。
因此,在第一种情况下你得到 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
.