为什么Python中的浮点除法用较小的数字更快?

时间:2016-02-15 21:00:11

标签: python performance math floating-point division

在回答this question的过程中,我遇到了一些我无法解释的事情。

给出以下Python 3.5代码:

import time

def di(n):
    for i in range(10000000): n / 101

i = 10
while i < 1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000:
    start = time.clock()
    di(i)
    end = time.clock()
    print("On " + str(i) + " " + str(end-start))
    i *= 10000

输出结果为:

On 10 0.546889
On 100000 0.545004
On 1000000000 0.5454929999999998
On 10000000000000 0.5519709999999998
On 100000000000000000 1.330797
On 1000000000000000000000 1.31053
On 10000000000000000000000000 1.3393129999999998
On 100000000000000000000000000000 1.3524339999999997
On 1000000000000000000000000000000000 1.3817269999999997
On 10000000000000000000000000000000000000 1.3412670000000002
On 100000000000000000000000000000000000000000 1.3358929999999987
On 1000000000000000000000000000000000000000000000 1.3773859999999996
On 10000000000000000000000000000000000000000000000000 1.3326890000000002
On 100000000000000000000000000000000000000000000000000000 1.3704769999999993
On 1000000000000000000000000000000000000000000000000000000000 1.3235019999999995
On 10000000000000000000000000000000000000000000000000000000000000 1.357647
On 100000000000000000000000000000000000000000000000000000000000000000 1.3341190000000012
On 1000000000000000000000000000000000000000000000000000000000000000000000 1.326544000000002
On 10000000000000000000000000000000000000000000000000000000000000000000000000 1.3671139999999973
On 100000000000000000000000000000000000000000000000000000000000000000000000000000 1.3630120000000012
On 1000000000000000000000000000000000000000000000000000000000000000000000000000000000 1.3600200000000022
On 10000000000000000000000000000000000000000000000000000000000000000000000000000000000000 1.3189189999999975
On 100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 1.3503469999999993

正如您所看到的,大概有两次:一次是较小的数字,另一次是较大的数字。

使用以下函数保存语义时,Python 2.7也会出现相同的结果:

def di(n):
    for i in xrange(10000000): n / 101.0

在同一台机器上,我得到:

On 10 0.617427
On 100000 0.61805
On 1000000000 0.6366
On 10000000000000 0.620919
On 100000000000000000 0.616695
On 1000000000000000000000 0.927353
On 10000000000000000000000000 1.007156
On 100000000000000000000000000000 0.98597
On 1000000000000000000000000000000000 0.99258
On 10000000000000000000000000000000000000 0.966753
On 100000000000000000000000000000000000000000 0.992684
On 1000000000000000000000000000000000000000000000 0.991711
On 10000000000000000000000000000000000000000000000000 0.994703
On 100000000000000000000000000000000000000000000000000000 0.978877
On 1000000000000000000000000000000000000000000000000000000000 0.982035
On 10000000000000000000000000000000000000000000000000000000000000 0.973266
On 100000000000000000000000000000000000000000000000000000000000000000 0.977911
On 1000000000000000000000000000000000000000000000000000000000000000000000 0.996857
On 10000000000000000000000000000000000000000000000000000000000000000000000000 0.972555
On 100000000000000000000000000000000000000000000000000000000000000000000000000000 0.985676
On 1000000000000000000000000000000000000000000000000000000000000000000000000000000000 0.987412
On 10000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0.997207
On 100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0.970129

为什么较小数字与较大数字的浮点除法之间存在这种一致差异?它是否与Python内部使用浮点数较小的数字和双倍的较大数字?

2 个答案:

答案 0 :(得分:7)

它更多地与Python存储精确整数作为Bignums。

在Python 2.7中,计算整数 a / float fb ,首先将整数转换为a浮动。如果整数存储为Bignum [注1]则需要更长时间。所以它不是具有差别成本的部门;它是整数(可能是Bignum)到double的转换。

Python 3对整数 a / float fb 执行相同的计算,但使用整数 a / 整数 b ,它会尝试计算最接近的可表示结果,这可能与天真{{1}略有不同}。 (这类似于经典的双舍入问题。)

如果float(a) / float(b)float(a)都是精确的(即float(b)a都不大于53位),那么天真的解决方案就可以了,结果只需要划分两个双精度浮点数。

否则,执行多精度除法以生成正确的53位尾数(指数单独计算),并将结果精确转换为浮点数。这种划分有两种可能性:快速跟踪如果b小到足以适合单个Bignum单位(适用于OP中的基准),以及{{1时更慢,一般的Bignum除法更大。

在上述情况中,没有观察到与硬件执行浮点除法的速度有关的速度差。对于原始的Python 3.5测试,差异与是否执行浮点或Bignum除法有关;对于Python 2.7的情况,差异与将Bignum转换为double的必要性有关。

感谢@MarkDickinson的澄清以及实现算法的the source code (with a long and useful comment)指针。

注释

  1. 在Python 3中,整数始终存储为Bignums。 Python 2具有b(64位整数)和b(Bignums)的单独类型。在实践中,由于Python 3经常使用优化算法,而Bignum只有一个&#34; leg&#34;,&#34; small&#34;之间的差异。和&#34;大&#34;整数仍然很明显。

答案 1 :(得分:1)

正如@rici所说,它是更大的整数格式。我将最初的10改为10.0 ......这是结果,时间没有显着变化。

On 10.0 1.12
On 100000.0 0.79
On 1000000000.0 0.79
On 1e+13 0.77
On 1e+17 0.78
On 1e+21 0.79
On 1e+25 0.77
On 1e+29 0.8
On 1e+33 0.77
On 1e+37 0.8
On 1e+41 0.78
On 1e+45 0.78
On 1e+49 0.78
On 1e+53 0.79
On 1e+57 0.77
On 1e+61 0.8
On 1e+65 0.77
On 1e+69 0.79
On 1e+73 0.77
On 1e+77 0.78
On 1e+81 0.78
On 1e+85 0.78
On 1e+89 0.77