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

为什么Python中的浮点除法使用较小的数字更快?,python,performance,math,floating-point,division,Python,Performance,Math,Floating Point,Division,在回答的过程中,我遇到了一些我无法解释的事情 给出以下Python 3.5代码: import time def di(n): for i in range(10000000): n / 101 i = 10 while i < 1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000: start = time.clock() d

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

给出以下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
如您所见,大致有两种情况:一种是较小的数字,另一种是较大的数字

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内部使用浮点数来表示较小的数字和使用双倍数来表示较大的数字有关系吗?

这与Python将精确整数存储为bignum有更大的关系

在Python 2.7中,整数a/浮点fb的计算从将整数转换为浮点开始。如果整数存储为Bignum[注1],则需要更长的时间。因此,并不是部门成本不同;它是整数(可能是Bignum)到double的转换

Python 3对整数a/浮点fb进行相同的计算,但对于整数a/整数b,它尝试计算最接近的可表示结果,这可能与原始的
浮点(a)/浮点(b)
略有不同。(这类似于经典的双舍入问题。)

如果
float(a)
float(b)
都是精确的(即
a
b
都不大于53位),那么朴素的解决方案有效,结果只需要两个双精度浮点的除法

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

在上述任何情况下,观察到的速度差都与硬件执行浮点除法的速度无关。对于原始的Python3.5测试,差异与是否执行浮点或Bignum除法有关;对于Python2.7,区别在于需要将Bignum转换为double

感谢@MarkDickinson的澄清,以及实现算法的指针


笔记
  • 在Python 3中,整数始终存储为bignum。Python 2对
    int
    (64位整数)和
    long
    (Bignums)有单独的类型。实际上,由于Python3经常在Bignum只有一个“leg”时使用优化算法,“小”和“大”整数之间的差异仍然很明显

  • 正如@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
    

    这还不是全部:在Python 3中,所有整数都存储为bignum,但在进行除法时,源代码中有一些特殊的大小写处理,在这种情况下,被除数和除数都可以用53位或更少的位数表示:在这种情况下,两者都可以转换为double,而不会损失精度,可以使用浮点除法来计算结果。在剩余的情况下,整数算术用于查找商。以下是相关的源代码以供参考:。下班后,我会写一个正确的答案。@MarkDickinson:谢谢。我希望我捕捉到的信息是正确的。啊,太美了。现在我不必写我自己的答案了!请注意,Python3中的
    n/101
    和Python2中的
    n/101.0
    没有相同的语义:前者对两个整数输入进行正确的四舍五入除法(对大的
    n
    内部使用整数算术)。后者将
    n
    转换为浮点数(在过程中可能会丢失精度),然后进行浮点除法。对于Python2中与Python3行为完全相同的行为,您可能希望从未来导入部分执行
    ,或者使用
    操作符.truediv
    。例如,如果这会产生影响,请尝试
    n=1163230408450025
    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