在Python中,用于浮点数的内置pow()和math.pow()之间的区别?
问题内容:
在使用两个 float 参数的情况下,Python的内置pow(x, y)
函数(没有第三个参数)返回的结果与的返回值是否有所不同?math.pow()
__
我问这个问题,因为文件的math.pow()
暗示pow(x, y)
(例如x**y
)在本质上是一样的math.pow(x, y)
:
math.pow(x,y)
将x提高到y。例外情况应尽可能遵循C99标准的附件“
F”。特别是,即使x为零或NaN,pow(1.0,x)和pow(x,0.0)始终返回1.0。如果x和y都是有限的,x是负数,并且y不是整数,则pow(x,y)是未定义的,并引发ValueError。在2.6版中进行了更改:1 nan和nan ** 0的结果不确定。
请注意最后一行:该文档暗示,行为math.pow()
是指数运算符的行为**
(因此是pow(x, y)
)。这是官方保证吗?
背景:我的目标是提供一个实现 这两个 内置的pow()
和math.pow()
对不确定性的数字 ,在相同的行为方式
与常规的Python花车(相同的数值结果,同样的例外,拐角情况相同的结果,等等)。我已经实现了一些效果很好的方法,但是有些极端情况需要处理。
问题答案:
快速检查
从签名中我们可以看出它们是不同的:
pow(x,y [,z])
math.pow(x,y)
另外,在shell中尝试它也会给您一个快速的想法:
>>> pow is math.pow
False
测试差异
了解这两个功能之间行为差异的另一种方法是测试它们:
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()
然后,我们可以注意到一些细微的差异。例如:
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
还有其他区别,并且上面的测试列表不完整(没有长数字,没有复数,等等),但这将为我们提供实用的列表,说明这两个函数的行为方式不同。我还建议扩展上述测试,以检查每个函数返回的类型。您可能会写类似的东西来创建两个函数之间差异的报告。
math.pow()
math.pow()
处理其参数的方法与内建**
或完全不同pow()
。这是以灵活性为代价的。在看看源,我们可以看到,参数math.pow()
被
直接转换为双打 :
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数学库。
内建的 pow()
另一方面,内置函数pow()
(与**
运算符相同)的行为却大不相同,它实际上使用了Objects自己的**
运算符实现,如果需要替换数字的或方法__pow__()
,最终用户可以重写该实现。__rpow__()``__ipow__()
对于内置类型,研究为两个数字类型实现的幂函数之间的差异是有启发性的,例如floats,long和complex。
覆盖默认行为
在此处描述了模拟数字类型。本质上,如果您要为不确定性的数字创建新的类型,则要做的是为类型提供__pow__()
,__rpow__()
并可能提供__ipow__()
方法。这将使您的号码可以与运营商一起使用:
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()
您将不得不对其进行修补以支持您的新类型:
def new_pow(a,b):
_a = Uncertain(a)
_b = Uncertain(b)
return _a ** _b
math.pow = new_pow
请注意,要执行此操作,您必须纠缠Uncertain
类以应对Uncertain
实例作为输入的__init__()