python - Python负指数与除法

标签 python floating-point

在Python中,我应该使用负指数还是除法?例:

>>> num = 2.0**-1


要么

>>> num = 1/2.0


我进行了一些测试,看来区别在于BINARY_POWER与BINARY_DIVIDE:

import dis

def exp_test(x):
    return x**-1

def div_test(x):
    return 1/x

print "Exp Test:"
dis.dis(exp_test)
print "Div Test:"
dis.dis(div_test)


输出:

Exp Test:
2           0 LOAD_FAST                0 (x)
            3 LOAD_CONST               1 (-1)
            6 BINARY_POWER        
            7 RETURN_VALUE 
Div Test:
2           0 LOAD_CONST               1 (1)
            3 LOAD_FAST                0 (x)
            6 BINARY_DIVIDE       
            7 RETURN_VALUE  


我在这里只考虑浮点值。我想我只需要担心浮点运算中通常会出现的差异。

最佳答案

如果您正在计算Python float x的倒数,我想不出任何理由更喜欢x**-1.0而不是1.0/x。从主观上讲,我认为后者更容易阅读。客观地讲,它可能会更快(因为它包装了一条简单的CPU指令,而不是对C库的pow函数的调用),并且更加准确(出于几乎相同的原因)。

请注意,我在两个表达式中都故意使用了1.0而不是1,这都是因为这样可以避免在x为浮点数的情况下进行额外的从int到float的转换,并且这将保证我在x恰好是一个int的情况下,在Python 2.7上获得一个适当的浮点除法而不是一个地板除法。

为了支持“更快”的主张,这是我的机器上的一些时间安排,包括同时具有11.0的变体。这是在Python 2.7下实现的,但结果在Python 3.4下却类似。

>>> import timeit
>>> timeit.timeit('x**-1', 'x=12.34')
0.08957314491271973
>>> timeit.timeit('x**-1.0', 'x=12.34')
0.08102011680603027
>>> timeit.timeit('1/x', 'x=12.34')
0.06166410446166992
>>> timeit.timeit('1.0/x', 'x=12.34')
0.04489898681640625


此处的除法形式具有相当明显的优势,并且使用1.0代替1可以明显提高速度。您的结果可能会有所不同。

注意:不要直接陷入诸如2**-11.0/2.0这样的定时表达式的陷阱。可以通过Python的窥孔优化器将这些表达式优化为编译时的常量,因此最终需要花费的时间就是检索常量0.5的时间。您可以使用标准库中的dis模块查看此内容:

>>> def half_via_pow(): return 2.0**-1.0
... 
>>> def half_via_div(): return 1.0/2.0
... 
>>> import dis
>>> dis.dis(half_via_pow)
  1           0 LOAD_CONST               3 (0.5)
              3 RETURN_VALUE        
>>> dis.dis(half_via_div)
  1           0 LOAD_CONST               1 (1.0)
              3 LOAD_CONST               2 (2.0)
              6 BINARY_DIVIDE       
              7 RETURN_VALUE        


上面显示了在Python 2.7上,计算2.0**-1.0被优化为一个常数,而除法1.0/2.0不是。在Python 3.4中,两者都被优化为一个常数。我怀疑Python 2.7窥孔优化器会避免优化除法,以便不必担心需要提高ZeroDivisionError的情况。无论哪种方式,对2.0**-1.0计时1.0/2.0都不能准确反映1.0/xx**-1.0的速度。将1.0/xx**-1.0相对应,并在设置步骤中为x提供一个值。

对于“更准确的”声明:1.0/x将解析为一条机器指令,几乎可以肯定的是,它将为您提供四舍五入的结果。相反,pow的库实现非常糟糕,并且经常会给出未正确舍入的结果。即使在最好的情况下,数学库的pow操作正确舍入,它仍然不会比除法结果更准确。

作为测试,我尝试了以下循环,比较1.0 / xx ** -1.0的随机x值:

>>> import random
>>> while True:
...     x = random.random()
...     print(repr(x))
...     assert 1.0/x == x**-1.0


果然,经过大约200次迭代,我得到了:

<around 200 lines of output omitted>
0.16606458447273365
0.6466363135038045
0.8650060330740814
Traceback (most recent call last):
  File "<stdin>", line 4, in <module>
AssertionError


看一下最后一个x值,我得到:

>>> x = 0.8650060330740814
>>> 1.0 / x
1.1560613010364489
>>> x ** -1.0
1.1560613010364487


粗略地检查一下1.0 / x的结果是否在此正确舍入,我们可以将浮点数转换为Fraction(进行精确转换),将倒数作为Fraction,然后转换回floatFraction的转换已正确舍入,并且不使用浮点算术来计算结果,因此在此检查中我们不依赖于浮点除法的精度。

>>> from fractions import Fraction
>>> float(1 / Fraction(x))
1.1560613010364489


float的精确倒数,计算为25个有效数字,是x。我们从1.156061301036448774438694取回的值,以相同的精度计算,是1.0 / x,而1.156061301036448885071195的值是< cc>,因此x ** -1.0仅比1.156061301036448663026590更接近真实值,但更接近。)

最后,对我而言,最重要的是,1.0 / x易于阅读,并且不需要花费很多心思即可解析。

总而言之,没有什么理由偏爱x ** -1.0形式。

关于python - Python负指数与除法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26185205/

相关文章:

python - 如何将参数传递给 QThread Worker 类?

c++ - Python 依赖,windows (CMake)

自定义 float 加法,实现数学表达式 - C

c++ - 在浮点标准之间转换

c# - C# 如何*确切地*将 double 转换为十进制?

python - Bigfloat - 精度错误

python - 无法在 Windows 中新安装的 Pycharm 中启动控制台

Python命令执行输出

python - 性能问题,Python vs C

c++ - 使用 CImg 编写 TIFF 浮点图像