Numpy以不同于python的方式进行回合

Ant*_*ins 7 python numpy rounding-error rounding

代码

import numpy as np
a = 5.92270987499999979065
print(round(a, 8))
print(round(np.float64(a), 8))
Run Code Online (Sandbox Code Playgroud)

5.92270987
5.92270988
Run Code Online (Sandbox Code Playgroud)

知道为什么吗?

在numpy来源中找不到任何相关内容.

更新:
我知道处理这个问题的正确方法是以这种差异无关紧要的方式构建程序.我做的.我在回归测试中偶然发现了它.

Update2:
关于@VikasDamodar评论.人们不应该相信这个repr()功能:

>>> np.float64(5.92270987499999979065)
5.922709875
>>> '%.20f' % np.float64(5.92270987499999979065)
'5.92270987499999979065'
Run Code Online (Sandbox Code Playgroud)

Update3:
在python3.6.0 x32,numpy 1.14.0,win64上测试.另外在python3.6.4 x64,numpy 1.14.0,debian.

Update4:
只是为了确定:

import numpy as np
a = 5.92270987499999979065
print('%.20f' % round(a, 8))
print('%.20f' % round(np.float64(a), 8))

5.92270987000000026512
5.92270988000000020435
Run Code Online (Sandbox Code Playgroud)

Update5:
以下代码演示了在不使用str以下情况下发生差异的阶段:

>>> np.float64(a) - 5.922709874
1.000000082740371e-09
>>> a - 5.922709874
1.000000082740371e-09
>>> round(np.float64(a), 8) - 5.922709874
6.000000496442226e-09
>>> round(a, 8) - 5.922709874
-3.999999442783064e-09
Run Code Online (Sandbox Code Playgroud)

很明显,在应用'round'之前,它们是相同的数字.

Update6:
与@ user2357112的答案相比,np.round大约比圆形慢4倍:

%%timeit a = 5.92270987499999979065
round(a, 8)

1.18 µs ± 26.5 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)  

%%timeit a = np.float64(5.92270987499999979065)
round(a, 8)

4.05 µs ± 43.9 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
Run Code Online (Sandbox Code Playgroud)

另外在我看来np.round做得更好的工作四舍五入到最近甚至比内置round:最初我得到这个5.92270987499999979065数字,除以11.84541975两个.

use*_*ica 5

float.__round__ 使用正确舍入的双到字符串算法特别注意生成正确舍入的结果。

NumPy 没有。该NumPy的文档提到,

由于 IEEE 浮点标准 [R9] 中小数的不精确表示以及按十的幂进行缩放时引入的错误,结果也可能令人惊讶。

这更快,但会产生更多的舍入误差。它会导致您观察到的错误,以及更明确地低于截止值的数字仍然被四舍五入的错误:

>>> x = 0.33499999999999996
>>> x
0.33499999999999996
>>> x < 0.335
True
>>> x < Decimal('0.335')
True
>>> x < 0.67/2
True
>>> round(x, 2)
0.33
>>> numpy.round(x, 2)
0.34000000000000002
Run Code Online (Sandbox Code Playgroud)

NumPy 的舍入时间变慢了,但这与哪种舍入算法较慢没有任何关系。NumPy 和常规 Python 数学之间的任何时间比较都将归结为 NumPy 已针对整个数组操作进行了优化。在单个 NumPy 标量上进行数学运算有很多开销,但是使用以下方法对整个数组进行numpy.round四舍五入比对浮点数列表进行四舍五入更容易round

In [6]: import numpy

In [7]: l = [i/7 for i in range(100)]

In [8]: a = numpy.array(l)

In [9]: %timeit [round(x, 1) for x in l]
59.6 µs ± 408 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

In [10]: %timeit numpy.round(a, 1)
5.27 µs ± 145 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
Run Code Online (Sandbox Code Playgroud)

至于哪个更准确,那肯定是float.__round__。您的数字接近5.92270987比5.92270988,这是廿四关系-to-甚至,不是圆的,一切到偶数。这里没有关系。