Il est bien connu que la comparaison des flottants pour l'égalité est un peu difficile en raison de problèmes d'arrondi et de précision.
Par exemple: https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
Quelle est la méthode recommandée pour gérer cela en Python?
Il y a sûrement une fonction de bibliothèque standard pour cela quelque part?
python
floating-point
Gordon Wrigley
la source
la source
all
,any
,max
,min
sont chacun essentiellement une seule ligne, et ils ne sont pas seulement fournis dans une bibliothèque, ils sont builtin fonctions. Donc, les raisons du BDFL ne sont pas cela. La seule ligne de code que la plupart des gens écrivent est assez simple et ne fonctionne souvent pas, ce qui est une bonne raison de fournir quelque chose de mieux. Bien sûr, tout module fournissant d'autres stratégies devrait également fournir des mises en garde décrivant quand elles sont appropriées, et plus important encore quand elles ne le sont pas. L'analyse numérique est difficile, ce n'est pas une grande honte que les concepteurs de langage n'essaient généralement pas d'outils pour l'aider.Réponses:
Python 3.5 ajoute les fonctions
math.isclose
etcmath.isclose
comme décrit dans PEP 485 .Si vous utilisez une version antérieure de Python, la fonction équivalente est donnée dans la documentation .
rel_tol
est une tolérance relative, elle est multipliée par la plus grande des grandeurs des deux arguments; au fur et à mesure que les valeurs deviennent plus grandes, la différence permise entre elles tout en les considérant comme égales.abs_tol
est une tolérance absolue qui est appliquée telle quelle dans tous les cas. Si la différence est inférieure à l'une ou l'autre de ces tolérances, les valeurs sont considérées comme égales.la source
a
oub
est unnumpy
array
,numpy.isclose
fonctionne.rel_tol
est une tolérance relative , elle est multipliée par la plus grande des grandeurs des deux arguments; au fur et à mesure que les valeurs deviennent plus grandes, la différence permise entre elles tout en les considérant comme égales.abs_tol
est une tolérance absolue qui est appliquée telle quelle dans tous les cas. Si la différence est inférieure à l'une ou l'autre de ces tolérances, les valeurs sont considérées comme égales.isclose
fonction (ci-dessus) n'est pas une implémentation complète .isclose
adhère toujours au critère le moins conservateur. Je ne le mentionne que parce que ce comportement est contre-intuitif pour moi. Si je devais spécifier deux critères, je m'attendrais toujours à ce que la plus petite tolérance remplace la plus grande.Est-ce que quelque chose d'aussi simple que le suivant n'est pas assez bon?
la source
abs(f1-f2) < tol*max(abs(f1),abs(f2))
. Ce type de tolérance relative est le seul moyen significatif de comparer les flottants en général, car ils sont généralement affectés par une erreur d'arrondi dans les petites décimales.>>> abs(0.04 - 0.03) <= 0.01
:, cela donneFalse
. J'utilisePython 2.7.10 [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
abs(f1 - f2) <= allowed_error
cela ne fonctionne pas comme prévu.Je conviens que la réponse de Gareth est probablement la plus appropriée en tant que fonction / solution légère.
Mais j'ai pensé qu'il serait utile de noter que si vous utilisez NumPy ou envisagez de le faire, il existe une fonction packagée pour cela.
Un petit avertissement cependant: l'installation de NumPy peut être une expérience non triviale selon votre plate-forme.
la source
pip
Windows.Utilisez le
decimal
module de Python , qui fournit laDecimal
classe.D'après les commentaires:
la source
Je ne connais rien dans la bibliothèque standard de Python (ou ailleurs) qui implémente la
AlmostEqual2sComplement
fonction de Dawson . Si c'est le genre de comportement que vous souhaitez, vous devrez l'implémenter vous-même. (Dans ce cas, plutôt que d'utiliser les hacks intelligents au niveau du bit de Dawson, vous feriez probablement mieux d'utiliser des tests plus conventionnels de la formeif abs(a-b) <= eps1*(abs(a)+abs(b)) + eps2
ou similaire. Pour obtenir un comportement semblable à Dawson, vous pourriez dire quelque chose commeif abs(a-b) <= eps*max(EPS,abs(a),abs(b))
pour certains petits correctifsEPS
; ce n'est pas exactement la même chose que Dawson, mais son esprit est similaire.la source
eps1
eteps2
définissez une tolérance relative et absolue: vous êtes prêt à autorisera
etb
à différer d'environeps1
fois leur taille, pluseps2
.eps
est une seule tolérance; vous êtes prêt à autorisera
etb
à différer d'environeps
fois leur taille, à condition que tout ce qui est de tailleEPS
ou plus petit soit supposé être de tailleEPS
. Si vous considérezEPS
qu'il s'agit de la plus petite valeur non dénormale de votre type à virgule flottante, cela est très similaire au comparateur de Dawson (à l'exception d'un facteur de 2 ^ # bits car Dawson mesure la tolérance en ulps).La sagesse courante selon laquelle les nombres à virgule flottante ne peuvent pas être comparés pour l'égalité est inexacte. Les nombres à virgule flottante ne sont pas différents des entiers: si vous évaluez "a == b", vous obtiendrez vrai si ce sont des nombres identiques et faux sinon (étant entendu que deux NaN ne sont bien sûr pas des nombres identiques).
Le vrai problème est le suivant: si j'ai fait quelques calculs et que je ne suis pas sûr que les deux chiffres que je dois comparer sont exactement corrects, alors quoi? Ce problème est le même pour les virgules flottantes que pour les entiers. Si vous évaluez l'expression entière "7/3 * 3", elle ne sera pas comparable à "7 * 3/3".
Supposons donc que nous ayons demandé "Comment comparer les entiers pour l'égalité?" Dans une telle situation. Il n'y a pas de réponse unique; ce que vous devez faire dépend de la situation spécifique, notamment du type d'erreurs que vous avez et de ce que vous souhaitez obtenir.
Voici quelques choix possibles.
Si vous voulez obtenir un résultat "vrai" si les nombres mathématiquement exacts sont égaux, vous pouvez essayer d'utiliser les propriétés des calculs que vous effectuez pour prouver que vous obtenez les mêmes erreurs dans les deux nombres. Si cela est possible et que vous comparez deux nombres qui résultent d'expressions qui donneraient des nombres égaux s'ils étaient calculés exactement, alors vous obtiendrez «vrai» de la comparaison. Une autre approche consiste à analyser les propriétés des calculs et à prouver que l'erreur ne dépasse jamais un certain montant, peut-être un montant absolu ou un montant relatif à l'une des entrées ou l'une des sorties. Dans ce cas, vous pouvez demander si les deux nombres calculés diffèrent d'au plus ce montant et renvoyer "vrai" s'ils se trouvent dans l'intervalle. Si vous ne pouvez pas prouver une erreur liée, vous pourriez deviner et espérer le meilleur. Une façon de deviner est d'évaluer de nombreux échantillons aléatoires et de voir quel type de distribution vous obtenez dans les résultats.
Bien sûr, puisque nous ne fixons l'exigence que vous devenez "vrai" que si les résultats mathématiquement exacts sont égaux, nous avons laissé ouverte la possibilité que vous obteniez "vrai" même s'ils sont inégaux. (En fait, nous pouvons satisfaire à l'exigence en retournant toujours "vrai". Cela rend le calcul simple mais n'est généralement pas souhaitable, donc je vais discuter de l'amélioration de la situation ci-dessous.)
Si vous voulez obtenir un résultat "faux" si les nombres mathématiquement exacts sont inégaux, vous devez prouver que votre évaluation des nombres donne des nombres différents si les nombres mathématiquement exacts sont inégaux. Cela peut être impossible à des fins pratiques dans de nombreuses situations courantes. Examinons donc une alternative.
Une condition utile pourrait être d'obtenir un résultat "faux" si les nombres mathématiquement exacts diffèrent de plus d'un certain montant. Par exemple, nous allons peut-être calculer où une balle lancée dans un jeu vidéo a voyagé, et nous voulons savoir si elle a frappé une batte. Dans ce cas, nous voulons certainement obtenir "vrai" si la balle frappe la batte, et nous voulons obtenir "faux" si la balle est loin de la batte, et nous pouvons accepter une réponse "vraie" incorrecte si la balle dans une simulation mathématiquement exacte a raté la chauve-souris, mais se situe à un millimètre près de la frappe. Dans ce cas, nous devons prouver (ou deviner / estimer) que notre calcul de la position de la balle et de la position de la batte a une erreur combinée d'au plus un millimètre (pour toutes les positions d'intérêt). Cela nous permettrait de toujours revenir "
Ainsi, la façon dont vous décidez quoi retourner lorsque vous comparez des nombres à virgule flottante dépend beaucoup de votre situation spécifique.
Quant à la façon dont vous allez prouver les limites d'erreur pour les calculs, cela peut être un sujet compliqué. Toute implémentation en virgule flottante utilisant la norme IEEE 754 en mode arrondi au plus proche renvoie le nombre à virgule flottante le plus proche du résultat exact pour toute opération de base (notamment multiplication, division, addition, soustraction, racine carrée). (En cas d'égalité, arrondissez pour que le bit faible soit égal.) (Faites particulièrement attention à la racine carrée et à la division; l'implémentation de votre langage peut utiliser des méthodes qui ne sont pas conformes à IEEE 754 pour celles-ci.) En raison de cette exigence, nous connaissons la l'erreur dans un seul résultat représente au plus la moitié de la valeur du bit le moins significatif. (Si c'était plus, l'arrondi serait allé à un nombre différent qui est dans la moitié de la valeur.)
Partir de là devient beaucoup plus compliqué; l'étape suivante exécute une opération où l'une des entrées a déjà une erreur. Pour les expressions simples, ces erreurs peuvent être suivies à travers les calculs pour atteindre une limite sur l'erreur finale. En pratique, cela ne se fait que dans quelques situations, comme le travail sur une bibliothèque de mathématiques de haute qualité. Et, bien sûr, vous avez besoin d'un contrôle précis sur exactement quelles opérations sont effectuées. Les langages de haut niveau donnent souvent beaucoup de mou au compilateur, vous ne savez donc peut-être pas dans quel ordre les opérations sont effectuées.
Il y a beaucoup plus qui pourrait être (et est) écrit sur ce sujet, mais je dois m'arrêter là. En résumé, la réponse est: il n'y a pas de routine de bibliothèque pour cette comparaison, car il n'y a pas de solution unique qui réponde à la plupart des besoins qui mérite d'être mise dans une routine de bibliothèque. (Si la comparaison avec un intervalle d'erreur relatif ou absolu vous suffit, vous pouvez le faire simplement sans routine de bibliothèque.)
la source
(7/3*3 == 7*3/3)
. Il a impriméFalse
.from __future__ import division
. Si vous ne le faites pas, il n'y a pas de nombres à virgule flottante et la comparaison se fait entre deux entiers.Si vous voulez l'utiliser dans le contexte testing / TDD, je dirais que c'est une manière standard:
la source
math.isclose () a été ajouté à Python 3.5 pour cela ( code source ). Voici un portage de celui-ci vers Python 2. Sa différence avec une ligne de Mark Ransom est qu'il peut gérer "inf" et "-inf" correctement.
la source
J'ai trouvé la comparaison suivante utile:
la source
str(.1 + .2) == str(.3)
renvoie False. La méthode décrite ci-dessus ne fonctionne que pour Python 2.Dans certains cas où vous pouvez affecter la représentation du numéro source, vous pouvez les représenter sous forme de fractions au lieu de flottants, en utilisant un numérateur et un dénominateur entiers. De cette façon, vous pouvez avoir des comparaisons exactes.
Voir le module Fraction de fractions pour plus de détails.
la source
J'ai aimé la suggestion de @Sesquipedal mais avec des modifications (un cas d'utilisation spécial lorsque les deux valeurs sont 0 renvoie False). Dans mon cas, j'étais sur Python 2.7 et j'utilisais juste une fonction simple:
la source
Utile dans le cas où vous voulez vous assurer que 2 nombres sont identiques 'jusqu'à la précision', pas besoin de spécifier la tolérance:
Trouver la précision minimale des 2 nombres
Arrondissez les deux à une précision minimale et comparez
Tel qu'il est écrit, ne fonctionne que pour les nombres sans le «e» dans leur représentation sous forme de chaîne (ce qui signifie 0,9999999999995e-4 <nombre <= 0,9999999999995e11)
Exemple:
la source
isclose(1.0, 1.1)
produitFalse
etisclose(0.1, 0.000000000001)
retourneTrue
.Pour comparer jusqu'à une décimale donnée sans
atol/rtol
:la source
C'est peut-être un hack un peu moche, mais cela fonctionne assez bien lorsque vous n'avez pas besoin de plus que la précision de flottement par défaut (environ 11 décimales).
La fonction round_to utilise la méthode de format de la classe str intégrée pour arrondir le flottant à une chaîne qui représente le flottant avec le nombre de décimales nécessaires, puis applique la fonction intégrée eval à la chaîne flottante arrondie pour revenir au type numérique flottant.
La fonction is_close applique simplement une conditionnelle simple au flottant arrondi.
Mettre à jour:
Comme suggéré par @stepehjfox, un moyen plus propre de construire une fonction rount_to en évitant "eval" utilise le formatage imbriqué :
En suivant la même idée, le code peut être encore plus simple en utilisant les nouvelles grandes chaînes f (Python 3.6+):
Donc, nous pourrions même tout résumer dans une fonction simple et propre 'is_close' :
la source
eval()
pour obtenir une mise en forme paramétrée. Quelque chose commereturn '{:.{precision}f'.format(float_num, precision=decimal_precision)
ça devrait le fairereturn '{:.{precision}}f'.format(float_num, precision=decimal_precision)
En termes d'erreur absolue, vous pouvez simplement vérifier
Quelques informations sur les raisons pour lesquelles les flotteurs agissent bizarrement en Python https://youtu.be/v4HhvoNLILk?t=1129
Vous pouvez également utiliser math.isclose pour les erreurs relatives
la source