在Python中,用于浮点数的内置pow()和math.pow()之间的区别是什么? [英] Difference between the built-in pow() and math.pow() for floats, in Python?
问题描述
在使用两个 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屋!