python x。** y与math.pow(x,y)的指数

Exponentials in python x.**y vs math.pow(x, y)

使用math.pow或**运算符哪个更有效? 我什么时候应该使用另一个?

到目前为止,我知道x**y可以返回intfloat(如果您使用小数)
函数pow将返回浮点数

1
2
3
4
5
import math

print math.pow(10, 2)

print 10. ** 2


使用动力运算符**将更快,因为它不会产生函数调用的开销。如果您反汇编Python代码,则可以看到以下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
>>> dis.dis('7. ** i')
  1           0 LOAD_CONST               0 (7.0)
              3 LOAD_NAME                0 (i)
              6 BINARY_POWER        
              7 RETURN_VALUE        
>>> dis.dis('pow(7., i)')
  1           0 LOAD_NAME                0 (pow)
              3 LOAD_CONST               0 (7.0)
              6 LOAD_NAME                1 (i)
              9 CALL_FUNCTION            2 (2 positional, 0 keyword pair)
             12 RETURN_VALUE        
>>> dis.dis('math.pow(7, i)')
  1           0 LOAD_NAME                0 (math)
              3 LOAD_ATTR                1 (pow)
              6 LOAD_CONST               0 (7)
              9 LOAD_NAME                2 (i)
             12 CALL_FUNCTION            2 (2 positional, 0 keyword pair)
             15 RETURN_VALUE

请注意,这里我使用变量i作为指数,因为像7. ** 5这样的常量表达式实际上是在编译时求值的。

现在,实际上,这种差异并不重要,正如您在计时时可以看到的那样:

1
2
3
4
5
6
7
>>> from timeit import timeit
>>> timeit('7. ** i', setup='i = 5')
0.2894785532627111
>>> timeit('pow(7., i)', setup='i = 5')
0.41218495570683444
>>> timeit('math.pow(7, i)', setup='import math; i = 5')
0.5655053168791255

因此,尽管powmath.pow的速度大约是其两倍,但它们仍然足够快而不必在意。除非您实际上可以将幂运算视为瓶颈,否则如果清晰度降低,就没有理由选择一种方法而不是另一种方法。这尤其适用,因为pow提供了集成的模运算。

Alfe在上述评论中提出了一个很好的问题:

timeit shows that math.pow is slower than ** in all cases. What is math.pow() good for anyway? Has anybody an idea where it can be of any advantage then?

math.pow与内置pow和幂运算符**的最大区别在于,它始终使用浮点语义。因此,如果由于某种原因要确保返回结果为浮点数,则math.pow将确保此属性。

我们来看一个示例:我们有两个数字ij,不知道它们是浮点数还是整数。但是我们希望浮点结果为i^j。那么我们有什么选择呢?

  • 我们可以将至少一个参数转换为浮点数,然后执行i ** j
  • 我们可以执行i ** j并将结果转换为浮点数(当ij为浮点数时,将自动使用浮点指数,因此结果相同)。
  • 我们可以使用math.pow

因此,让我们测试一下:

1
2
3
4
5
6
7
8
>>> timeit('float(i) ** j', setup='i, j = 7, 5')
0.7610865891750791
>>> timeit('i ** float(j)', setup='i, j = 7, 5')
0.7930400942188385
>>> timeit('float(i ** j)', setup='i, j = 7, 5')
0.8946636625872202
>>> timeit('math.pow(i, j)', setup='import math; i, j = 7, 5')
0.5699394063529439

如您所见,math.pow实际上更快!如果考虑一下,函数调用的开销现在也消失了,因为在所有其他替代方案中,我们必须调用float()

此外,可能需要注意,通过为自定义类型实现特殊的__pow__(和__rpow__)方法,可以覆盖**pow的行为。因此,如果出于任何原因(无论出于何种原因)都不希望这样做,则使用math.pow不会这样做。


仅针对协议:**运算符调用内置的pow函数,如果前两个参数是整数类型,则该函数接受可选的第三个参数(模数)。

因此,如果要通过幂计算余数,请使用内置函数。 math.pow可能会给您错误的结果:

1
2
3
4
5
6
7
import math

base = 13
exp = 100
mod = 2
print math.pow(base, exp) % mod
print pow(base, exp, mod)

运行此命令时,我在第一种情况下得到的0.0显然是不正确的,因为13是奇数(因此,所有这些都是整数)。 math.pow版本使用有限的精度,这会导致错误。

为了公平起见,我们必须说math.pow可以快得多:

1
2
3
import timeit
print timeit.timeit("math.pow(2, 100)",setup='import math')
print timeit.timeit("pow(2, 100)")

这是我得到的输出:

1
2
0.240936803195
1.4775809183

一些在线示例

  • http://ideone.com/qaDWRd(math.pow的余数错误)
  • http://ideone.com/g7J9Un(在int值上使用pow时性能较低)
  • http://ideone.com/KnEtXj(浮点值pow时性能略低)


好吧,它们确实是用于不同的任务。

当您需要整数运算时,请使用pow(等效于x ** y,带有两个参数)。

并使用math.pow,如果其中一个参数为float,并且您希望float输出。

有关powmath.pow之间差异的讨论,请参见此问题。


**确实比math.pow()更快,但是如果您想要一个简单的二次函数(如您的示例中所示),则使用产品的速度甚至更快。

1
10.*10.

会更快

1
10.**2

一次操作(使用timeit)的差异并不大且不明显,但是对于大量操作而言,差异可能很大。


pow()函数将允许您添加第三个参数作为模数。

例如:我最近在执行操作时遇到内存错误

2**23375247598357347582 % 23375247598357347583

相反,我做了:

pow(2, 23375247598357347582, 23375247598357347583)

这仅需几毫秒即可返回,而不是简单的指数所花费的大量时间和内存。 因此,当处理大数和并行模数时,pow()效率更高,但是当处理小数而无模数时,**效率更高。