logaritmo libreria descargar built python math

libreria - python 3.6 math module



¿Diferencia entre el pow() incorporado y math.pow() para flotantes, en Python? (3)

¿Hay alguna diferencia en los resultados devueltos por pow(x, y) incorporado de Python (ningún tercer argumento) y los valores devueltos por math.pow() , en el caso de dos argumentos float ?

Estoy haciendo esta pregunta porque la documentation para math.pow() implica que pow(x, y) (es decir, x**y ) es esencialmente lo mismo que math.pow(x, y) :

math.pow (x, y)

Retorno x elevado a la potencia y. Los casos excepcionales siguen, en la medida de lo posible, el Anexo "F" de la norma C99. En particular, pow (1.0, x) y pow (x, 0.0) siempre devuelven 1.0, incluso cuando x es cero o NaN. Si tanto x como y son finitos, x es negativo e y no es un entero, entonces pow (x, y) no está definido, y aumenta ValueError.

Modificado en la versión 2.6: el resultado de 1 ** nan y nan ** 0 no estaba definido.

Observe la última línea: la documentación implica que el comportamiento de math.pow() es el del operador de exponenciación ** (y por lo tanto de pow(x, y) ). ¿Esto está oficialmente garantizado?

Antecedentes: Mi objetivo es proporcionar una implementación tanto del pow() math.pow() como del math.pow() para números con incertidumbre que se comporta de la misma manera que con los flotantes normales de Python (mismos resultados numéricos, mismas excepciones, mismos resultados para casos de esquina, etc.). Ya he implementado algo que funciona bastante bien, pero hay algunos casos de esquina que deben ser manejados.


Comprobación rápida

A partir de las firmas, podemos decir que son diferentes:

pow (x, y [, z])

math.pow (x, y)

Además, probarlo en el caparazón le dará una idea rápida:

>>> pow is math.pow False

Probando las diferencias

Otra forma de entender las diferencias de comportamiento entre las dos funciones es probarlas:

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()

Entonces podemos notar algunas diferencias sutiles. Por ejemplo:

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

Hay otras diferencias, y la lista de pruebas anterior no está completa (sin números largos, sin complejos, etc.), pero esto nos dará una lista pragmática de cómo las dos funciones se comportan de manera diferente. También recomendaría extender la prueba anterior para verificar el tipo que devuelve cada función. Probablemente puedas escribir algo similar que cree un informe de las diferencias entre las dos funciones.

math.pow()

math.pow() maneja sus argumentos de forma muy diferente a los incorporados ** o pow() . Esto tiene un costo de flexibilidad. Al echar un vistazo a la fuente , podemos ver que los argumentos para math.pow() se math.pow() directamente en dobles :

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

Los controles se llevan a cabo contra los dobles para la validez, y luego el resultado se pasa a la biblioteca matemática C subyacente.

pow() incorporado pow()

El pow() incorporado (igual que el operador ** ) por otro lado se comporta de manera muy diferente, en realidad usa la propia implementación del operador ** los Objetos, que puede ser anulada por el usuario final si es necesario reemplazando un número __pow__() , __rpow__() o __ipow__() , método.

Para los tipos incorporados, es instructivo estudiar la diferencia entre la función de potencia implementada para dos tipos numéricos, por ejemplo, floats , long y complex .

Reemplazando el comportamiento predeterminado

Emular tipos numéricos se describe here . esencialmente, si está creando un nuevo tipo para números con incertidumbre, lo que tendrá que hacer es proporcionar los __pow__() , __rpow__() y posiblemente __ipow__() para su tipo. Esto permitirá que sus números sean utilizados con el operador:

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 )

Para sobrescribir math.pow() tendrá que parchearlo para que sea compatible con su nuevo tipo:

def new_pow(a,b): _a = Uncertain(a) _b = Uncertain(b) return _a ** _b math.pow = new_pow

Tenga en cuenta que para que esto funcione, tendrá que disputar la clase Uncertain para hacer frente a una instancia Uncertain como una entrada para __init__()


El pow estándar de Python incluye un truco simple que hace que pow(2, 3, 2) más rápido que (2 ** 3) % 2 (por supuesto, solo notarás eso con números grandes).

Otra gran diferencia es cómo las dos funciones manejan diferentes formatos de entrada.

>>> 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

Sin embargo, no tengo idea de por qué alguien preferiría math.pow sobre pow .


math.pow() convierte implícitamente sus argumentos a float :

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

pero el pow incorporado no:

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

Mi objetivo es proporcionar una implementación de pow () incorporado y de math.pow () para números con incertidumbre

Puedes sobrecargar pow y ** definiendo los métodos __pow__ y __rpow__ para tu clase.

Sin embargo, no puedes sobrecargar math.pow (sin hacks como math.pow = pow ). Puede hacer que una clase se pueda usar con math.pow definiendo una conversión de __float__ , pero luego perderá la incertidumbre asociada a sus números.