68 votes

Différence entre les fonctions intégrées pow () et math.pow () pour les flotteurs, en Python?

Est-il une différence dans les résultats retournés par Python intégré dans l' pow(x, y) (pas de troisième argument) et les valeurs retournées par math.pow(), dans le cas de deux float arguments.

Je pose cette question parce que la documentation pour math.pow() implique qu' pow(x, y) (c - x**y) est essentiellement le même que l' math.pow(x, y):

les mathématiques.pow(x, y)

De retour de x à la puissance y. Des cas exceptionnels suivez l'Annexe " F " du standard C99 aussi loin que possible. Dans en particulier, pow(1.0, x) et pow(x, 0.0) renvoient toujours 1.0, même lorsque x est un zéro ou un NaN. Si x et y sont finies, x est négatif, et y n'est pas un entier puis pow(x, y) n'est pas défini, et soulève ValueError.

Modifié dans la version 2.6: Les résultats de 1**nan et nan**0 n'est pas défini.

Remarque la dernière ligne: la documentation implique que le comportement de l' math.pow() est celui de l'opérateur exponentiel ** (et donc de l' pow(x, y)). Est-ce officiellement garanti?

Contexte: Mon objectif est de fournir une implémentation de la fois le haut- pow() et de la math.pow() pour les nombres, de l'incertitude, qui se comporte de la même manière qu'avec des régulière Python flotte (même résultats numériques, même exceptions près, les mêmes résultats pour le cas du coin, etc.). J'ai déjà mis en place quelque chose qui fonctionne assez bien, mais il y a quelques cas particuliers qui doivent être traitées.

59voto

brice Points 5740

Vérification Rapide

De le des signatures, nous pouvons dire qu'ils sont différents:

pow(x, y, z])

les mathématiques.pow(x, y)

Aussi, en essayant dans le shell va vous donner une petite idée:

>>> pow is math.pow
False

Tester les différences

Une autre façon de comprendre les différences de comportement entre les deux fonctions est de les tester:

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

Ensuite, nous pouvons remarquer quelques différences subtiles. Par exemple:

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

Il y a d'autres différences, et le test de la liste ci-dessus n'est pas complet (pas de numéros longs, pas de complexes, etc...), mais cela va nous donner une liste pratique de la façon dont les deux fonctions se comportent différemment. Je vous recommande également d'étendre le test ci-dessus pour vérifier le type de chaque fonction renvoie. On pourrait peut-être écrire quelque chose de semblable qui crée un rapport de la différence entre les deux fonctions.

math.pow()

math.pow() gère ses arguments de façon très différente de la builtin ** ou pow(). Cela se fait au détriment de la souplesse. Avoir un coup d'oeil à la source, nous pouvons voir que la arguments au math.pow() sont exprimés directement à doubles:

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

Les contrôles sont ensuite menées contre le double pour la validité et le résultat est transmis à la sous-jacentes C de la bibliothèque math.

builtin pow()

Le haut- pow() (la même que l' ** opérateur) sur l'autre main se comporte de manière très différente, en fait, il utilise les Objets propres de la mise en œuvre de l' ** de l'opérateur, qui peut être remplacée par l'utilisateur final en cas de nécessité par le remplacement d'un certain nombre de __pow__(), __rpow__() ou __ipow__(), méthode.

Pour les types intégrés, il est instructif d'étudier la différence entre la fonction de puissance mis en œuvre pour les deux types numériques, par exemple, des flotteurs, long et complexe.

Overridding le comportement par défaut

L'émulation de types numériques est décrit ici. essentiellement, si vous êtes à la création d'un nouveau type pour les nombres à l'incertitude, ce que vous avez à faire est de fournir à l' __pow__(), __rpow__() et, éventuellement, __ipow__() méthodes pour votre type. Cela permettra à vos numéros pour être utilisé avec l'opérateur:

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
    )

Afin de remplacer math.pow() vous aurez à monkey patch à l'appui de votre nouveau type:

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

math.pow = new_pow

Notez que pour que cela fonctionne, vous devez régler l' Uncertain classe pour faire face à un Uncertain exemple comme un intrant __init__()

34voto

dan04 Points 33306

math.pow() convertit implicitement ses arguments au float:

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

mais le haut- pow n'est pas:

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

Mon objectif est de fournir une mise en œuvre de la built-in pow() et de mathématiques.pow() pour les numéros avec l'incertitude

Vous pouvez surcharger pow et ** en définissant __pow__ et __rpow__ méthodes pour votre classe.

Cependant, vous ne pouvez pas surcharger math.pow (sans hacks comme math.pow = pow). Vous pouvez faire une classe utilisable avec math.pow par la définition d'un __float__ de conversion, mais alors vous allez perdre de l'incertitude attachée à vos chiffres.

12voto

Tom van der Woerdt Points 18645

Le standard pow Python inclut un hack simple qui rend pow(2, 3, 2) plus rapide que (2 ** 3) % 2 (bien sûr, vous ne le remarquerez qu'avec de grands nombres).

Une autre grande différence est la manière dont les deux fonctions gèrent des formats de saisie différents.

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

Cependant, je n'ai aucune idée pourquoi quiconque préférerait math.pow sur pow .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X