在Python中,用于浮点数的内置pow()和math.pow()之间的区别是什么? [英] Difference between the built-in pow() and math.pow() for floats, in Python?

查看:704
本文介绍了在Python中,用于浮点数的内置pow()和math.pow()之间的区别是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在使用两个 float 参数的情况下,Python内置的pow(x, y)(无第三个参数)返回的结果与math.pow()返回的值是否有所不同.

Is there a difference in the results returned by Python's built-in pow(x, y) (no third argument) and the values returned by math.pow(), in the case of two float arguments.

我问这个问题是因为math.pow()文档表示pow(x, y)(即x**y)与math.pow(x, y)基本上相同:

I am asking this question because the documentation for math.pow() implies that pow(x, y) (i.e. x**y) is essentially the same as math.pow(x, y):

math.pow(x,y)

将x返回到幂y.例外情况 尽可能遵循C99标准的附件"F".在 特别是pow(1.0,x)和pow(x,0.0)总是返回1.0,即使x 是零或NaN.如果x和y都是有限的,则x为负,并且y 不是整数,则pow(x,y)未定义,并引发ValueError.

Return x raised to the power y. Exceptional cases follow Annex ‘F’ of the C99 standard as far as possible. In particular, pow(1.0, x) and pow(x, 0.0) always return 1.0, even when x is a zero or a NaN. If both x and y are finite, x is negative, and y is not an integer then pow(x, y) is undefined, and raises ValueError.

在2.6版中进行了更改:1 nan和nan ** 0的结果是不确定的.

Changed in version 2.6: The outcome of 1**nan and nan**0 was undefined.

请注意最后一行:文档暗示math.pow()的行为是幂运算符**(因此是pow(x, y))的行为.这是官方保证吗?

Note the last line: the documentation implies that the behavior of math.pow() is that of the exponentiation operator ** (and therefore of pow(x, y)). Is this officially guaranteed?

背景:我的目标是为内在不确定性以相同方式表现的数字提供内置pow()math.pow()的实现.与常规Python浮点数一样(相同的数值结果,相同的异常,针对极端情况的相同结果,等等).我已经已实现,效果很好,但是有一些

Background: My goal is to provide an implementation of both the built-in pow() and of math.pow() for numbers with uncertainty that behaves in the same way as with regular Python floats (same numerical results, same exceptions, same results for corner cases, etc.). I have already implemented something that works quite well, but there are some corner cases that need to be handled.

推荐答案

快速检查

从签名中我们可以看出它们是不同的:

Quick Check

From the signatures, we can tell that they are different:

pow(x,y [,z])

math.pow(x,y)

另外,在shell中尝试它也会给您一个快速的想法:

Also, trying it in the shell will give you a quick idea:

>>> pow is math.pow
False

测试差异

了解这两个功能之间行为差异的另一种方法是对其进行测试:

Testing the differences

Another way to understand the differences in behaviour between the two functions is to test for them:

import math
import traceback
import sys

inf = float("inf")
NaN = float("nan")

vals = [inf, NaN, 0.0, 1.0, 2.2, -1.0, -0.0, -2.2, -inf, 1, 0, 2]

tests = set([])

for vala in vals:
  for valb in vals:
    tests.add( (vala, valb) )
    tests.add( (valb, vala) )


for a,b in tests:
  print("math.pow(%f,%f)"%(a,b) )
  try:
    print("    %f "%math.pow(a,b))
  except:
    traceback.print_exc()

  print("__builtins__.pow(%f,%f)"%(a,b) )
  try:
    print("    %f "%__builtins__.pow(a,b))
  except:
    traceback.print_exc()

然后我们可以注意到一些细微的差异.例如:

We can then notice some subtle differences. For example:

math.pow(0.000000,-2.200000)
    ValueError: math domain error

__builtins__.pow(0.000000,-2.200000)
    ZeroDivisionError: 0.0 cannot be raised to a negative power

还有其他差异,并且上面的测试列表不完整(没有长数字,没有复数等),但这将为我们提供实用的列表,说明这两个函数的行为方式不同.我还建议扩展上述测试,以检查每个函数返回的类型.您可能会写类似的东西来创建两个函数之间差异的报告.

There are other differences, and the test list above is not complete (no long numbers, no complex, etc...), but this will give us a pragmatic list of how the two functions behave differently. I would also recommend extending the above test to check for the type that each function returns. You could probably write something similar that creates a report of the differences between the two functions.

math.pow()处理其参数的方法与内置的**pow()完全不同.这是以灵活性为代价的.看看,我们可以看到math.pow()的参数将直接转换为双精度:

math.pow() handles its arguments very differently from the builtin ** or pow(). This comes at the cost of flexibility. Having a look at the source, we can see that the arguments to math.pow() are cast directly to doubles:

static PyObject *
math_pow(PyObject *self, PyObject *args)
{
    PyObject *ox, *oy;
    double r, x, y;
    int odd_y;

    if (! PyArg_UnpackTuple(args, "pow", 2, 2, &ox, &oy))
        return NULL;
    x = PyFloat_AsDouble(ox);
    y = PyFloat_AsDouble(oy);
/*...*/

然后针对双精度进行检查以检查有效性,然后将结果传递到基础C数学库.

The checks are then carried out against the doubles for validity, and then the result is passed to the underlying C math library.

另一方面,内置的pow()(与**运算符相同)在行为上有很大不同,它实际上使用对象自己的**运算符实现,如果最终用户可以重写此实现,则需要通过替换数字的__pow__()__rpow__()__ipow__()方法.

The built-in pow() (same as the ** operator) on the other hand behaves very differently, it actually uses the Objects's own implementation of the ** operator, which can be overridden by the end user if need be by replacing a number's __pow__(), __rpow__() or __ipow__(), method.

对于内置类型,研究为两个数字类型实现的幂函数之间的差异很有帮助,例如,复杂.

For built-in types, it is instructive to study the difference between the power function implemented for two numeric types, for example, floats, long and complex.

此处中描述了模拟数字类型.本质上,如果要为不确定性的数字创建新类型,则必须为类型提供__pow__()__rpow__()以及可能的__ipow__()方法.这将允许您的号码与运营商一起使用:

Emulating numeric types is described here. essentially, if you are creating a new type for numbers with uncertainty, what you will have to do is provide the __pow__(), __rpow__() and possibly __ipow__() methods for your type. This will allow your numbers to be used with the operator:

class Uncertain:
  def __init__(self, x, delta=0):
    self.delta = delta
    self.x = x
  def __pow__(self, other):
    return Uncertain(
      self.x**other.x, 
      Uncertain._propagate_power(self, other)
    )
  @staticmethod
  def _propagate_power(A, B):
    return math.sqrt(
      ((B.x*(A.x**(B.x-1)))**2)*A.delta*A.delta +
      (((A.x**B.x)*math.log(B.x))**2)*B.delta*B.delta
    )

要覆盖math.pow(),您必须先对其进行猴子补丁以支持您的新类型:

In order to override math.pow() you will have to monkey patch it to support your new type:

def new_pow(a,b):
    _a = Uncertain(a)
    _b = Uncertain(b)
    return _a ** _b

math.pow = new_pow

请注意,要使其正常工作,您必须扭绞Uncertain类以应对Uncertain实例作为__init__()

Note that for this to work you'll have to wrangle the Uncertain class to cope with an Uncertain instance as an input to __init__()

这篇关于在Python中,用于浮点数的内置pow()和math.pow()之间的区别是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆