【问题标题】:Exponentials in python: x**y vs math.pow(x, y)python中的指数:x**y vs math.pow(x, y)
【发布时间】:2014-01-25 00:37:30
【问题描述】:

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

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

import math

print( math.pow(10, 2) )

print( 10. ** 2 )

【问题讨论】:

  • 为什么不timeit 来了解一下?
  • timeit 表明math.pow 在所有情况下都比** 慢。 math.pow() 到底有什么用?有没有人知道它在哪里可以有任何优势?
  • @Alfe 你是如何衡量的,还是我误解了你的in all cases?我see cases 其中math.pow 要快得多。
  • @Wolf,你呢?您的链接指向powmath.pow 的比较,但我说的是math.pow** 运算符的比较。您可以通过添加第三个版本来完成您的比较,然后** 再次击败所有其他选项:import timeit; print timeit.timeit("math.pow(2, 100)",setup='import math'), timeit.timeit("pow(2, 100)"), timeit.timeit("2 ** 100")0.170357942581 1.00546097755 0.013473033905
  • 但我不得不承认,结果并不完全一样。 math.pow 总是 返回float(也用于int 输入),而** 返回其输入的类型(好吧,混合输入导致float)。但这仍然不是使用 math.pow 的好理由,恕我直言。

标签: python math built-in pow


【解决方案1】:

使用幂运算符** 会更快,因为它不会产生函数调用的开销。反汇编 Python 代码可以看到:

>>> 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 这样的常量表达式实际上是在编译时计算的。

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

>>> 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 在上面的 cmets 中问了一个很好的问题:

timeit 表明math.pow 在所有情况下都比** 慢。 math.pow() 到底有什么用?有没有人知道它在哪里可以有任何优势?

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

让我们考虑一个例子:我们有两个数字,ij,但不知道它们是浮点数还是整数。但我们希望得到i^j 的浮点结果。那么我们有哪些选择呢?

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

所以,让我们测试一下:

>>> 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 的行为可以通过实现自定义类型的特殊__pow__(和__rpow__)方法来覆盖。因此,如果您不想这样做(无论出于何种原因),使用 math.pow 将不会这样做。

【讨论】:

  • You got +1 for " the behavior of ** and pow can be overridden":如果编写模块,我相信防止来自用户环境的意外非常重要......跨度>
【解决方案2】:

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

例如:我最近在做的时候遇到了内存错误

2**23375247598357347582 % 23375247598357347583

相反,我做了:

pow(2, 23375247598357347582, 23375247598357347583)

这仅在几毫秒内返回,而不是普通指数所花费的大量时间和内存。因此,在处理大数和并行模数时,pow() 更有效,但在处理没有模数的较小数字时,** 更有效。

【讨论】:

    【解决方案3】:

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

    10.*10.
    

    会更快

    10.**2
    

    使用一次操作(使用timeit)时差异不大且不明显,但使用大量操作时差异可能很大。

    【讨论】:

      【解决方案4】:

      仅用于协议:** 运算符等效于 built-in pow function 的双参数版本,pow 函数 接受可选的第三个参数(模数),如果第一个两个参数都是整数。

      因此,如果您打算从幂计算余数,请使用内置函数。 math.pow 会给你合理大小的参数错误的结果:

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

      当我运行这个时,我在第一种情况下得到了0.0,这显然不是真的,因为 13 是奇数(因此它都是整数幂)。 math.pow 版本使用 IEEE-754 双精度(52 位尾数,略小于 16 位小数)的有限精度,这会导致此处出错。

      为了公平起见,不得不说,math.pow还可以更快:

      >>> import timeit
      >>> min(timeit.repeat("pow(1.1, 9.9)", number=2000000, repeat=5))
      0.3063715160001266
      >>> min(timeit.repeat("math.pow(1.1, 9.9)", setup="import math", number=2000000, repeat=5))
      0.2647279420000359
      

      math.pow 函数在工程应用中具有(并且仍然)其优势,但对于数论应用,您应该使用内置的 pow 函数。


      一些在线示例


      更新(不可避免的更正):
      我删除了math.pow(2,100)pow(2,100) 的时间比较,因为math.pow 给出了错误的结果,而例如pow(2,50)math.pow(2,50) 之间的比较本来是公平的(尽管@987654345 的实际使用不是@-module 函数)。我添加了一个更好的以及导致math.pow限制的细节。

      【讨论】:

        【解决方案5】:

        嗯,它们是用于不同的任务的,真的。

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

        如果任一参数是浮点数,并且您想要浮点数输出,则使用math.pow

        有关powmath.pow 之间差异的讨论,请参阅此question

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-06-26
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-11-06
          相关资源
          最近更新 更多