Разница между встроенным pow () и математикой.pow() для поплавков, в Python?
есть ли разница в результатах, возвращаемых встроенным Python pow(x, y)
(нет третьего аргумента) и значения, возвращаемые math.pow()
в случае двух float аргументов.
Я задаю этот вопрос, потому что документация на math.pow()
означает, что pow(x, y)
(т. е. x**y
) по сути то же самое, что math.pow(x, y)
:
математика.pow (x, y)
возвращает X в степени у. Исключительные ситуации насколько это возможно, следуйте приложению " F " к стандарту С99. В в частности, pow (1.0, x) и pow (x, 0.0) всегда возвращают 1.0, даже если x это ноль или НАН. Если и x, и y конечны, то x отрицательно, а y не является целым числом, тогда pow (x, y) не определен и вызывает ValueError.
изменено в версии 2.6: результат 1 * * nan и nan* * 0 не был определен.
обратите внимание на последнюю строку: документация подразумевает, что поведение math.pow()
is что оператора возведения в степень **
(и, следовательно,pow(x, y)
). Это официально гарантировано?
Предыстория: моя цель-обеспечить реализацию и встроенный pow()
и math.pow()
для чисел с неопределенностью что ведет себя точно так же как и с обычными Python поплавками (те же численные результаты, те же исключения, те же результаты для угловых случаев и т. д.). У меня есть уже реализовано то, что работает вполне хорошо, но есть некоторые случаи, которые должны быть обработаны.
3 ответа:
Быстрая Регистрация
по подписям можно сказать, что они разные:
pow (x, y[, z])
математика.pow (x, y)
кроме того, попытка его в оболочке даст вам быструю идею:
>>> 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()
are бросьте сразу к двойникам: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 math.
builtin
pow()
встроенный
pow()
(то же самое, что**
оператор) с другой стороны ведет себя совсем по-другому, он фактически использует собственную реализацию объектов**
оператор, который может быть переопределен конечным пользователем, если это необходимо, заменив число__pow__()
,__rpow__()
или__ipow__()
, способ.для встроенных типов полезно изучить разницу между функцией мощности, реализованной для двух числовых типов, для например, терки,долго и комплекс.
переопределение поведения по умолчанию
описана эмуляция числовых типов здесь. по сути, если вы создаете новый тип для чисел с неопределенностью, то вам нужно будет предоставить
__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 )
In чтобы переопределить
math.pow()
вы должны будете обезьяна патч его для поддержки вашего нового типа:def new_pow(a,b): _a = Uncertain(a) _b = Uncertain(b) return _a ** _b math.pow = new_pow
обратите внимание, что для этого вам придется спорить
Uncertain
класс, чтобы справиться сUncertain
экземпляр в качестве входных данных для__init__()
math.pow()
неявно преобразует свои аргументыfloat
:>>> 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
(без хаков, какmath.pow = pow
). Вы можете сделать класс пригодным для использования сmath.pow
определение__float__
преобразование, но тогда вы потеряете неопределенность, связанную с вашими номерами.
питон
pow
включает в себя простой хак, который делает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
overpow
.