70

2 つのfloat引数の場合、 Python の組み込みpow(x, y)(3 番目の引数なし)によって返される結果と によって返される値に違いはありますか。math.pow()

のドキュメントが(つまり) が本質的に と同じであることをmath.pow()暗示しているため、この質問をしています。pow(x, y)x**ymath.pow(x, y)

math.pow(x, y)

x の y 乗を返します。例外的なケースは、可能な限り C99 標準の Annex 'F' に従います。特に、pow(1.0, x) と pow(x, 0.0) は、x がゼロまたは NaN の場合でも、常に 1.0 を返します。x と y の両方が有限で、x が負で、y が整数でない場合、pow(x, y) は定義されておらず、ValueError が発生します。

バージョン 2.6 で変更: 1**nan と nan**0 の結果は未定義でした。

math.pow()最後の行に注意してください: ドキュメントは、 の動作が指数演算子の動作であることを暗示しています**(したがって、 のpow(x, y))。これは公式に保証されていますか?

背景: 私の目標は、通常の Python 浮動小数点数と同じように動作する(同じ数値結果、同じ例外、コーナー ケースの同じ結果など)不確実性を伴うビルトインとfor numberの両方の実装を提供することです。私はすでに非常にうまく機能するものを実装しましたが、処理する必要があるいくつかのまれなケースがあります。pow()math.pow()

4

4 に答える 4

60

クイックチェック

署名から、それらが異なることがわかります。

pow(x, y[, z])

math.pow(x, y)

また、シェルで試してみると、簡単なアイデアが得られます。

>>> pow is math.pow
False

違いをテストする

2 つの関数の動作の違いを理解する別の方法は、それらをテストすることです。

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

他にも違いがあり、上記のテスト リストは完全ではありません (長い数値がない、複雑でないなど) が、これにより、2 つの関数の動作がどのように異なるかの実用的なリストが得られます。上記のテストを拡張して、各関数が返す型を確認することもお勧めします。おそらく、2 つの関数の相違点のレポートを作成する同様のものを作成できます。

math.pow()

math.pow()**は、組み込みのorとは非常に異なる方法で引数を処理しますpow()。これには、柔軟性が犠牲になります。sourceを見ると、引数math.pow()double に直接キャストされていることがわかります。

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);
/*...*/

次に、double の有効性についてチェックが実行され、その結果が基になる C 数学ライブラリに渡されます。

ビルトインpow()

一方、ビルトインpow()(演算子と同じ**) は非常に異なる動作をします。実際にはオブジェクト独自の**演算子の実装を使用します。これは、必要に応じてエンド ユーザーが数値の__pow__(),__rpow__()または__ipow__(), メソッドを置き換えることでオーバーライドできます。

組み込み型の場合、 floatslong、およびcomplexなどの 2 つの数値型に実装された累乗関数の違いを調べることは有益です。

デフォルトの動作のオーバーライド

数値型のエミュレートについては、こちらで説明しています。基本的に、不確実性を伴う数値の新しい型を作成する場合は、型に__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__()

于 2012-04-23T14:50:00.490 に答える
35

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() の両方の実装を、不確かさのある数値に提供することです。

クラスのandメソッドを定義することにより、 powandをオーバーロードできます。**__pow____rpow__

ただし、オーバーロードすることはできませんmath.pow(のようなハックがなければmath.pow = pow)。math.pow変換を定義することでクラスを使用可能にすることができ__float__ますが、数値に付随する不確実性が失われます。

于 2012-04-23T15:01:30.293 に答える
12

Python の標準には、よりも高速化powする単純なハックが含まれています (もちろん、大きな数の場合にのみ気付くでしょう)。pow(2, 3, 2)(2 ** 3) % 2

もう 1 つの大きな違いは、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.powpow

于 2012-04-23T14:51:17.153 に答える