在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上获得一个适当的浮点除法而不是一个地板除法。
为了支持“更快”的主张,这是我的机器上的一些时间安排,包括同时具有1
和1.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**-1
或1.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/x
与x**-1.0
的速度。将1.0/x
与x**-1.0
相对应,并在设置步骤中为x
提供一个值。对于“更准确的”声明:
1.0/x
将解析为一条机器指令,几乎可以肯定的是,它将为您提供四舍五入的结果。相反,pow
的库实现非常糟糕,并且经常会给出未正确舍入的结果。即使在最好的情况下,数学库的pow
操作正确舍入,它仍然不会比除法结果更准确。作为测试,我尝试了以下循环,比较
1.0 / x
和x ** -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
,然后转换回float
到Fraction
的转换已正确舍入,并且不使用浮点算术来计算结果,因此在此检查中我们不依赖于浮点除法的精度。>>> 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/