égalité de point flottant en Python et en général
J'ai un morceau de code qui se comporte différemment selon que je passe par un dictionnaire pour obtenir les facteurs de conversion ou si je les utiliser directement.
Le morceau de code suivant apparaîtra à l'impression 1.0 == 1.0 -> False
Mais si vous remplacez factors[units_from]
avec 10.0
et factors[units_to ]
avec 1.0 /2.54
il permet d'imprimer 1.0 == 1.0 -> True
#!/usr/bin/env python
base = 'cm'
factors = {
'cm' : 1.0,
'mm' : 10.0,
'm' : 0.01,
'km' : 1.0e-5,
'in' : 1.0 / 2.54,
'ft' : 1.0 / 2.54 / 12.0,
'yd' : 1.0 / 2.54 / 12.0 / 3.0,
'mile' : 1.0 / 2.54 / 12.0 / 5280,
'lightyear' : 1.0 / 2.54 / 12.0 / 5280 / 5.87849981e12,
}
# convert 25.4 mm to inches
val = 25.4
units_from = 'mm'
units_to = 'in'
base_value = val / factors[units_from]
ret = base_value * factors[units_to ]
print ret, '==', 1.0, '->', ret == 1.0
Permettez-moi d'abord de dire que je suis assez sûr que ce qui se passe ici. Je l'ai vu avant dans C, est tout simplement jamais en Python, mais depuis le Python est implémenté en C, nous le voyons.
Je sais que les nombres à virgule flottante va changer les valeurs à partir d'un PROCESSEUR inscrire dans la mémoire cache et le dos. Je sais que la comparaison de ce que devrait être l'égalité des deux variables retourne false si l'un d'entre eux a été paginées, tandis que l'autre est resté résident dans un registre.
Questions
- Quelle est la meilleure façon d'éviter les problèmes de ce genre?... En Python ou en général.
- Suis-je en train de faire quelque chose de complètement faux?
Note De Côté
C'est évidemment le cadre d'une dépouillé exemple, mais ce que j'essaie de faire est de venir avec des avec des classes de longueur, de volume, etc, qui peuvent comparer à d'autres objets de la même classe, mais avec des unités différentes.
Questions Rhétoriques
- Si c'est potentiellement un problème dangereux, car il rend les programmes se comportent dans une undetermanistic la question, devrait les compilateurs d'avertir ou d'erreur lorsqu'ils détectent que vous êtes à la vérification de l'égalité de flotteurs
- Devrait compilateurs une option pour remplacer toutes flottent des vérifications d'égalité avec un "assez proche" de la fonction?
- Ne compilateurs déjà le faire et je ne peux pas trouver les informations.
source d'informationauteur eric.frederich
Vous devez vous connecter pour publier un commentaire.
Comme l'a montré la comparaison de deux flotteurs (ou doubles, etc) peut être problématique. En général, au lieu de comparer exacte de l'égalité, ils doivent être vérifiés à l'encontre d'une erreur liée. Si ils sont dans l'erreur lié, ils sont considérés comme égaux.
Qui est beaucoup plus facile à dire qu'à faire. La nature de la virgule flottante faire une correction d'une erreur liée à rien. Une petite erreur lié (comme 2*float_epsilon) fonctionne bien lorsque les valeurs sont proches de 0,0, mais échouera si la valeur sont près de 1000. Une erreur liée pour des valeurs aussi grandes que 1 000 000 d'.0 sera beaucoup trop laxiste pour les valeurs proches de 0.0.
La meilleure solution est de connaître le nom de domaine de votre de mathématiques et de choisir un approprié tre tenu sur une base de cas par cas.
Lorsque cela est impossible ou si vous êtes paresseux, Unités dans la Dernière Place (Ptd) est un roman et de la solution solide. Les détails sont très impliqués, vous pouvez en lire plus ici.
L'idée de base est cela, un nombre à virgule flottante dispose de deux pièces, la mantisse et l'exposant. Généralement, les erreurs d'arrondi seulement changer la mantisse par quelques étapes. Lorsque la valeur est proche de 0.0 ceux étapes sont exactement float_epsilon. Lorsque la valeur à virgule flottante est plus proche à 1 000 000, les étapes sont presque aussi grand que 1.
Google test utilise ULP à comparer des nombres à virgule flottante. Ils ont choisi une valeur par défaut de 4 ULPs pour deux nombres à virgule flottante à être contre l'égalité. Vous pouvez également utiliser leur code de référence pour construire votre propre ULP style float comparateur.
La différence est que si vous remplacez
factors[units_to ]
avec1.0 /2.54
vous êtes en train de faire:Avec le dictionnaire, vous êtes en train de faire:
L'ordre de l'arrondissement de questions. C'est plus facile de voir si vous n':
Noter qu'il n'y a pas de non-déterministe ou un comportement indéfini. Il y a une norme, la norme IEEE-754, dont la mise en œuvre doit être conforme à (ne pas prétendre qu'ils ont toujours ne).
Je ne pense pas qu'il devrait y avoir un automatique assez proche de remplacement. C'est souvent un moyen efficace de traiter le problème, mais il devrait être au programmeur de décider si et comment l'utiliser.
Enfin, il y a des options pour une précision arbitraire de l'arithmétique, y compris python-gmp et décimal. Pense que si vous avez réellement besoin ces, parce qu'ils ont un impact significatif sur les performances.
Il n'y a pas de problème de déplacement entre les registres et la mémoire cache. Vous pensez peut-être de la x86 80 bits précision étendue.
Permettez-moi de répondre en disant que vous devriez lire David Goldberg classique Ce Que Tout Informaticien Devez Savoir À Propos De L'Arithmétique À Virgule Flottante.
Comme certains autres commentateurs ont déjà dit, la différence que vous remarquerez est intrinsèquement à cause de la virgule flottante modèle et n'a rien à voir avec les registres, cache ou mémoire.
Selon la virgule flottante modèle, 2,54 est représenté comme
Cette représentation n'est cependant pas exact:
Maintenant, l'expression de l'évaluation est en fait:
Le problème réside dans le 1/2.54:
Mais ce que vous attendez est
Pour répondre à vos questions:
Merci pour vos réponses. La plupart étaient très bonnes et les liens donc je vais juste dire que et répondre à ma propre question.
Caspin posté cette lien.
Il a également mentionné que Google Tests utilisés ULP comparaison et quand j'ai regardé le code google j'ai vu qu'ils ont mentionné le même lien exact pour cygnus-logiciel.
J'ai fini la mise en œuvre de certains des algorithmes en C comme une extension Python et puis, plus tard vu que je pouvais le faire en pure Python. Le code est affiché en dessous.
En fin de compte, je vais probablement juste le vent en ajoutant ULP différences à mon sac à malices.
Il était intéressant de voir comment beaucoup de floating points sont entre ce qui devrait être de deux nombres égaux qui n'a jamais quitté la mémoire. L'un des articles ou le google code, je l'ai lu a dit que 4 est un bon numéro... mais ici, j'ai été en mesure de frapper 10.
Aussi intéressant est de savoir comment beaucoup de floating points, il y a entre un nombre égal quand l'un d'eux est écrit comme un string et de lire de nouveau.
si j'exécute ce
il imprime "pas égal", ce qui est mal, mais comme
donne le flotteur d'erreur comme -1.11022302 e-16 je viens de faire quelque chose comme ceci:
sinon, vous pouvez les convertir à la fois pour les cordes, je suppose:
Ce problème? Vous travaillez avec des mesures physiques. Sauf si vous avez quelques vraiment des équipements sophistiqués, l'erreur dans vos mesures va être de plusieurs ordres de grandeur supérieure à virgule flottante epsilon. Alors pourquoi écrire du code qui dépend du nombre exact de 16 chiffres significatifs?
Si elle le fait, vous devez obtenir certains résultats étranges:
Si vous pensez que c'est assez dur pour stocker flotte dans un dictionnaire maintenant, essayez avec un non-transitive
==
opérateur de! Et la performance serait horrible, parce que la seule façon de garantirx == y
→hash(x) == hash(y)
serait pour chaque flotteur d'avoir le même code de hachage. Et ce serait incompatible avec ints.Afin de comparer les flotteurs, en général, de comparer la valeur absolue de la différence des flotteurs d'un delta qui est assez petit pour s'adapter à vos besoins.
Questions Rhétoriques
Je suppose que c'est la même chose pour python, comme le delta à utiliser pour la comparaison peut varier, il doit être à l'opérateur de choisir. Ce qui signifie que rien de bon par défaut de la transformation peuvent être fournis de manière entièrement automatique.
Qui est sans doute un Python bug. Ce numéro a été écrit avec de douze chiffres. Deux identifier de manière unique un double de 64 bits (Python de type float) vous avez besoin de dix-sept chiffres de la mantisse. Si Python imprimé de ses numéros à 17 chiffres de précision ensuite, vous être garanti de revenir exactement la même valeur.
La question de précision est discutée au:
http://randomascii.wordpress.com/2012/03/08/float-precisionfrom-zero-to-100-digits-2/
L'accent est mis sur 32 bits à virgule flottante (ce qui nécessite un nombre de neuf chiffres de la mantisse pour identifier de manière unique chaque numéro), mais il mentionne brièvement double, et le fait qu'elle nécessite des 17 chiffres de la mantisse.