Python 中浮点数的内置 pow() 和 math.pow() 之间有何区别?

2025-03-20 08:47:00
admin
原创
50
摘要:问题描述:在有两个浮点参数的情况下,Python 内置pow(x, y)(没有第三个参数)返回的结果和返回的值是否有区别?math.pow()我问这个问题是因为的文档暗示(即)math.pow()本质上与相同:pow(x, y)`x**y`math.pow(x, y)数学.pow(x,y)返回 x 的 y 次...

问题描述:

在有两个浮点参数的情况下,Python 内置pow(x, y)(没有第三个参数)返回的结果和返回的值是否有区别?math.pow()

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

数学.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。

在 2.6 版更改: 1nan 和 nan0 的结果未定义。

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

背景:我的目标是为不确定的数字提供内置pow()的实现,其行为方式与常规 Python 浮点数相同(相同的数值结果、相同的异常、极端情况相同的结果等)。我已经实现了一些效果很好的方法,但有一些极端情况需要处理。math.pow()


解决方案 1:

快速检查

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

pow(x,y[,z])

数学.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()(与运算符相同)的行为非常不同,它实际上使用对象自己的运算符实现,如果需要,最终用户可以通过替换数字的、或方法来覆盖它。**`**__pow__()__rpow__()`__ipow__()

对于内置类型,研究两种数字类型(例如float、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__()

解决方案 2:

math.pow()隐式地将其参数转换为float

>>> from decimal import Decimal
>>> from fractions import Fraction
>>> math.pow(Fraction(1, 3), 2)
0.1111111111111111
>>> math.pow(Decimal(10), -1)
0.1

但内置功能pow没有:

>>> pow(Fraction(1, 3), 2)
Fraction(1, 9)
>>> pow(Decimal(10), -1)
Decimal('0.1')

我的目标是为不确定的数字提供内置 pow() 和 math.pow() 的实现

您可以通过为您的类定义和方法来重载pow和。**`__pow__`__rpow__

但是,您无法重载math.pow(没有像 这样的 hack )。您可以通过定义转换math.pow = pow来使类可用,但这样您将失去与数字相关的不确定性。math.pow`__float__`

解决方案 3:

Python 的标准pow包含一个简单的 hack,可以使其pow(2, 3, 2)速度更快(2 ** 3) % 2(当然,只有在数字很大时你才会注意到这一点)。

另一个很大的区别是这两个函数如何处理不同的输入格式。

>>> pow(2, 1+0.5j)
(1.8810842093664877+0.679354250205337j)
>>> math.pow(2, 1+0.5j)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can't convert complex to float

不过,我不知道为什么有人会math.pow更喜欢pow

解决方案 4:

只需添加 %timeit 比较

In [1]: def pair_generator(): 
    ...:     yield (random.random()*10, random.random()*10) 
    ...:   

In [2]: %timeit [a**b for a, b in pair_generator()]                                                                    
538 ns ± 1.94 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

In [3]: %timeit [math.pow(a, b) for a, b in pair_generator()]                                                          
632 ns ± 2.77 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   2482  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1533  
  PLM(产品生命周期管理)项目对于企业优化产品研发流程、提升产品质量以及增强市场竞争力具有至关重要的意义。然而,在项目推进过程中,范围蔓延是一个常见且棘手的问题,它可能导致项目进度延迟、成本超支以及质量下降等一系列不良后果。因此,有效避免PLM项目范围蔓延成为项目成功的关键因素之一。以下将详细阐述三大管控策略,助力企业...
plm系统   0  
  PLM(产品生命周期管理)项目管理在企业产品研发与管理过程中扮演着至关重要的角色。随着市场竞争的加剧和产品复杂度的提升,PLM项目面临着诸多风险。准确量化风险优先级并采取有效措施应对,是确保项目成功的关键。五维评估矩阵作为一种有效的风险评估工具,能帮助项目管理者全面、系统地评估风险,为决策提供有力支持。五维评估矩阵概述...
免费plm软件   0  
  引言PLM(产品生命周期管理)开发流程对于企业产品的全生命周期管控至关重要。它涵盖了从产品概念设计到退役的各个阶段,直接影响着产品质量、开发周期以及企业的市场竞争力。在当今快速发展的科技环境下,客户对产品质量的要求日益提高,市场竞争也愈发激烈,这就使得优化PLM开发流程成为企业的必然选择。缺陷管理工具和六西格玛方法作为...
plm产品全生命周期管理   0  
热门文章
项目管理软件有哪些?
曾咪二维码

扫码咨询,免费领取项目管理大礼包!

云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用