Numpy:点(a,b)和(a * b)之间的差异.sum()

时间:2013-08-07 01:02:12

标签: python numpy scipy precision floating-accuracy

对于1-D numpy数组,这两个表达式应该产生相同的结果(理论上):

(a*b).sum()/a.sum()
dot(a, b)/a.sum()

后者使用dot()并且速度更快。但哪一个更准确?为什么呢?

以下是一些背景信息。

我想使用numpy计算样本的加权方差。 我在another answer中找到了dot()表达式,并注释说它应该更准确。但是没有给出任何解释。

1 个答案:

答案 0 :(得分:9)

Numpy dot是调用您在编译时链接(或构建自己的)的BLAS库的例程之一。这一点的重要性在于BLAS库可以使用乘法累加运算(通常是Fused-Multiply Add)来限制计算执行的舍入次数。

采取以下措施:

>>> a=np.ones(1000,dtype=np.float128)+1E-14 
>>> (a*a).sum()  
1000.0000000000199948
>>> np.dot(a,a)
1000.0000000000199948

不完全,但足够接近。

>>> a=np.ones(1000,dtype=np.float64)+1E-14
>>> np.dot(a,a)
1000.0000000000176  #off by 2.3948e-12
>>> (a*a).sum()
1000.0000000000059  #off by 1.40948e-11

np.dot(a, a)对两者的准确性会更准确,因为它使用的是原始(a*a).sum()的浮点舍入次数的大约一半。

Nvidia的一本书有4位精度的例子。 rn代表4轮到最近的4位数:

x = 1.0008
x2 = 1.00160064                    #    true value
rn(x2 − 1) = 1.6006 × 10−4         #    fused multiply-add
rn(rn(x2) − 1) = 1.6000 × 10−4     #    multiply, then add

当然浮点数不会四舍五入到基数10的第16位小数位,但是你明白了。

使用一些额外的伪代码将np.dot(a,a)置于上述表示法中:

out=0
for x in a:
    out=rn(x*x+out)   #Fused multiply add

虽然(a*a).sum()是:

arr=np.zeros(a.shape[0])   
for x in range(len(arr)):
    arr[x]=rn(a[x]*a[x])

out=0
for x in arr:
    out=rn(x+out)

从中可以很容易地看出,使用(a*a).sum()np.dot(a,a)相比,该数字的舍入次数是其两倍。这些微小的差异总结可以改变答案。可以找到其他exmaples here