web-dev-qa-db-fra.com

Test d'égalité en virgule flottante

Existe-t-il une fonction permettant de tester l’égalité approximative en virgule flottante en python? Quelque chose comme,

 def approx_equal(a, b, tol):
     return abs(a - b) < tol

Mon cas d'utilisation est similaire à la façon dont la bibliothèque de tests C++ de Google, gtest.h, définit EXPECT_NEAR.

Voici un exemple:

def bernoulli_fraction_to_angle(fraction):
    return math.asin(sqrt(fraction))
def bernoulli_angle_to_fraction(angle):
    return math.sin(angle) ** 2
def test_bernoulli_conversions():
    assert(approx_equal(bernoulli_angle_to_fraction(pi / 4), 0.5, 1e-4))
    assert(approx_equal(
              bernoulli_fraction_to_angle(bernoulli_angle_to_fraction(0.1)),
                0.1, 1e-4))
36
Neil G
  • Pour comparer les nombres, il existe math.isclose selon PEP 485 depuis Python 3.5.
  • Pour comparer des nombres ou des tableaux, il existe numpy.allclose.
  • Pour tester des nombres ou des tableaux, il existe numpy.testing.assert_allclose
51
Neil G

Une autre approche consiste à calculer le changement relatif (ou différence relative) des deux nombres, qui est "utilisé pour comparer deux quantités en tenant compte de la" taille "des objets comparés". Les deux formules mentionnées dans l'article de Wikipédia pourraient être utilisées dans des comparaisons comme celle qui suit en Python, qui traitent également les cas où l'une des valeurs comparées, ou les deux, vaut zéro:

def approx_equal(a, b, tol):
    return abs(a-b) <= max(abs(a), abs(b)) * tol

def approx_equal(a, b, tol):
    return abs(a-b) <= (abs(a)+abs(b))/2 * tol

La valeur calculée dans les deux cas est une fraction sans unité. Dans le premier cas, la valeur de référence est la valeur absolue maximale des deux nombres et dans le second, leur valeur absolue moyenne. L'article traite de chacun plus en détail, ainsi que leurs avantages et inconvénients. Ce dernier peut être converti en pourcentage de différence si multiplié par 100 avant la comparaison (avec tol devenant une valeur en pourcentage). Notez que l'article suggère que si la valeur changeante "est un pourcentage lui-même, il est préférable de parler de son changement en utilisant des points de pourcentage", c'est-à-dire un changement absolu.

Ces deux méthodes (évidemment) nécessitent un peu plus de calcul que la simple prise de la valeur absolue de la différence des deux nombres, ce qui pourrait être une considération.

19
martineau

Existe-t-il une fonction permettant de tester l’égalité approximative en virgule flottante en python? 

Il ne peut y avoir a function, car la définition dépend du contexte.

def eq( a, b, eps=0.0001 ):
    return abs(a - b) <= eps

Ça ne marche pas toujours. Il y a des circonstances où

def eq( a, b, eps=0.0001 ):
     return abs( a - b ) / abs(a) <= eps

pourrait être plus approprié.

De plus, il y a le toujours populaire.

def eq( a, b, eps=0.0001 ):
    return abs(math.log( a ) - math.log(b)) <=  eps

Ce qui pourrait être plus approprié.

Je ne vois pas comment vous pouvez demander une fonction (unique) combine toutes les alternatives mathématiques. Puisque cela dépend de l'application.

8
S.Lott

Si j'étais vous, j'utiliserais simplement ce que vous avez écrit et l'insérerais dans un module séparé (peut-être avec d'autres utilitaires dont vous avez besoin pour lequel Python n'a pas d'implémentation) ou au début du code qui l'exige.

Vous pouvez également utiliser une expression lambda (une de mes fonctionnalités de langage préférées, mais probablement moins claire):

approx_equal = lambda a, b, t: abs(a - b) < t
5
Rafe Kettler

Comparer les flottants pour l'égalité est généralement une mauvaise idée. Même avec la fonction de tolérance que vous utilisez, ce n'est pas vraiment ce que vous voulez faire. 

Si vous voulez utiliser des floats, une option raisonnable consiste à refactoriser votre algorithme pour utiliser des inégalités, a < b, car il est plus susceptible de faire ce que vous attendez, avec beaucoup moins de faux négatifs ou de positifs, et surtout, cela signifie que vous n'avez deviner à quel point ils doivent être égaux pour être égaux.

Si vous ne pouvez pas faire cela, une autre option consiste à utiliser une représentation exacte. Si votre algorithme est composé uniquement d'opérations arithmétiques (+, -, * et /), vous pouvez utiliser une représentation rationnelle, fournie par fractions.Fraction, ou peut-être que decimal.Decimal est ce que vous voulez (avec des calculs financiers, par exemple).

Si votre algorithme ne peut pas être exprimé facilement avec une représentation de précision arbitraire, vous pouvez également gérer l'erreur d'arrondi de manière explicite avec une arithmétique d'intervalle, par exemple avec ce module .

Selon le tutoriel :

... Bien que les nombres ne puissent pas être rapprochés des valeurs exactes prévues, la fonction round () peut être utile pour les arrondis ultérieurs, de sorte que les résultats contenant des valeurs inexactes soient comparables les uns aux autres ...

C'est donc comme cela que je définis les fonctions "isclose" en Python:

def isclose(a, b, ndigits):
   return round(a-b, ndigits) == 0

J'utilise habituellement 5 comme ndigits; Cependant, cela dépend de la précision que vous attendez. 

0
Saeid BK