Comment dois-je faire floating point de comparaison?
Je suis en train d'écrire un peu de code où j'ai quelque chose le long des lignes de:
double a = SomeCalculation1();
double b = SomeCalculation2();
if (a < b)
DoSomething2();
else if (a > b)
DoSomething3();
Puis dans d'autres endroits, j'ai peut-être besoin de faire de l'égalité:
double a = SomeCalculation3();
double b = SomeCalculation4();
if (a == 0.0)
DoSomethingUseful(1 / a);
if (b == 0.0)
return 0; //or something else here
En bref, j'ai beaucoup de calcul en virgule flottante en passe et j'ai besoin de faire des comparaisons pour les conditions. Je ne peux pas le convertir en entier mathématiques parce que une telle chose est dénuée de sens dans ce contexte.
J'ai lu avant que virgule flottante comparaisons peuvent être peu fiables, car vous pouvez avoir des choses comme ça:
double a = 1.0 / 3.0;
double b = a + a + a;
if ((3 * a) != b)
Console.WriteLine("Oh no!");
En bref, je voudrais savoir: Comment puis-je fiable comparer des nombres à virgule flottante (moins que, plus que, de l'égalité)?
Le numéro de plage que j'utilise est à peu près de 10E-14 à 10E6, donc je n'ai besoin de travailler avec de petits nombres, ainsi que la grande.
J'ai balisé à ce que la langue agnostique parce que je suis intéressé à savoir comment je peux faire ce quelle que soit la langue que j'utilise.
- Il n'y a aucun moyen de le faire de manière fiable lors de l'utilisation de nombres à virgule flottante. Il y aura toujours des nombres que pour l'ordinateur sont égaux si, en réalité, ne le sont pas (dire 1E+100, 1E+100+1), et vous aussi généralement des résultats de calcul que pour l'ordinateur, ne sont pas égaux si, en réalité, sont (voir un des commentaires à nelhage de réponse). Vous aurez à choisir entre les deux, vous avez le désir de moins en moins.
- D'autre part, si vous, dire, faire affaire uniquement avec des nombres rationnels, vous pourriez mettre en œuvre certains nombre rationnel de l'arithmétique de base sur les nombres entiers, puis deux nombres sont considérées comme égales si l'un des deux numéros peuvent être annulés en bas à l'autre.
- Et bien, actuellement je suis en train de travailler d'une simulation. L'endroit j'ai l'habitude de faire ces comparaisons est liée à la variable de pas de temps (pour la résolution de certains ode). Il y a quelques cas où j'ai besoin de vérifier si le pas de temps pour un objet est égal, inférieur ou supérieur à un autre objet du pas de temps.
- le Plus efficace pour les float et double comparaison
- Pourquoi ne pas utiliser des tableaux? stackoverflow.com/questions/28318610/...
Vous devez vous connecter pour publier un commentaire.
Comparant pour le plus grand/plus petit n'est pas vraiment un problème, sauf si vous travaillez à droite au bord de l'float/double précision limite.
Pour un "flou est égal à" titre de comparaison, cette (code Java, devrait être facile à adapter) est ce que j'ai trouvé pour La Virgule Flottante Guide, après beaucoup de travail et en tenant compte de beaucoup de critiques:
Il est livré avec une suite de tests. Vous devez immédiatement rejeter toute solution qui ne fonctionne pas, car il est pratiquement voué à l'échec dans certains cas limites comme ayant une valeur de 0, deux valeurs très faibles en face de zéro, ou infinis.
Une alternative (voir lien ci-dessus pour plus de détails) est de convertir les chars modèles de bits en entier et de tout accepter au sein d'un fixe entier distance.
En tout cas, il n'est probablement pas une solution qui est parfait pour toutes les applications. Idéalement, vous souhaitez développer/adapter votre propre avec une suite de tests couvrant votre cas d'utilisation.
return diff < epsilon*the_smallest_normalized_fp_number
en casa*b == 0
?else if (a * b == 0)
, mais alors votre commentaire sur la même ligne esta or b or both are zero
. Mais ne sont-ce pas ces deux choses différentes? E. g., sia == 1e-162
etb == 2e-162
alors la conditiona * b == 0
sera vrai.abs(a-b)<eps
réponses ici. Deux questions: (1) ne Serait-il pas préférable de changer tous les<
s à<=
s, permettant ainsi de "zéro-eps" les comparaisons, l'équivalent des comparaisons exactes? (2) ne Serait-il pas préférable d'utiliserdiff < epsilon * (absA + absB);
au lieu dediff / (absA + absB) < epsilon;
(dernière ligne) -- ?Float.MIN_NORMAL
? Le les plus proches des constantes j'ai trouvé ont été:FLT_MIN
etFLT_TRUE_MIN
.define FLT_TRUE_MIN __FLT_DENORM_MIN__
, de sorte que c'est apparemment anormale, doncFLT_MIN
doit être l'équivalent deFloat.MIN_NOMAL
- mais pour en être certain, il suffit de regarder la valeur 🙂FLT_MIN = 1.175494e-38
etFLT_TRUE_MIN = 1.401298e-45
. En outre,FLT_MIN.isNormal == true
etFLT_TRUE_MIN.isNormal == false
.nearlyEqual(a, b, eps)
donne la même réponse quenearlyEqual(a-b, 0, eps)
pour tous lesa
etb
. Vous avez besoin de connaître la tolérance dans les numéros que vous travaillez avec, et ensuite utiliser un simple approche absolue, telle que stackoverflow.com/a/19050413/908621.J'ai eu le problème de la Comparaison des nombres à virgule flottante
A < B
etA > B
Voici ce qui semble fonctionner:
La fab--valeur absolue-- prend en charge s'ils sont essentiellement égaux.
Nous avons à choisir un niveau de tolérance pour comparer des nombres réels. Par exemple,
Une note. Votre exemple est plutôt drôle.
Certains maths ici
Oh, oui..
Voulez-vous dire
TL;DR
Graphiques, s'il vous plaît?
Lors de la comparaison des nombres à virgule flottante, il y a deux "modes".
Le premier est le relative mode, d'où la différence entre
x
ety
est considéré comme étant relativement à leur amplitude|x| + |y|
. Lors de la parcelle en 2D, il donne le profil suivant, où le vert signifie l'égalité dex
ety
. (J'ai pris unepsilon
de 0,5 à des fins d'illustration).Le mode relatif est ce qui est utilisé pour "normal" ou "assez grand" floating points des valeurs. (Plus sur cela plus tard).
Le deuxième est un absolue mode, quand il suffit de comparer leur différence d'un nombre fixe. Il donne le profil suivant (de nouveau avec un
epsilon
de 0.5 et unerelth
de 1 pour l'illustration).Ce mode absolu de comparaison est ce qui est utilisé pour les "petites" valeurs à virgule flottante.
Maintenant, la question est, comment pouvons-nous combiner ces deux schémas de réponse.
Michael Borgwardt réponse, le commutateur est basé sur la valeur de
diff
, qui devrait être en dessous derelth
(Float.MIN_NORMAL
dans sa réponse). Ce commutateur horaire est indiqué éclos dans le graphique ci-dessous.Parce que
relth * epsilon
est plus petit querelth
, les taches vertes ne collent pas ensemble, qui à son tour donne la solution d'un mauvais propriété: on peut trouver des triplets de nombres tels quex < y_1 < y_2
et pourtantx == y2
maisx != y1
.Prendre cet exemple frappant:
Nous avons
x < y1 < y2
, et en faity2 - x
est plus de 2000 fois plus grande quey1 - x
. Et pourtant, avec la solution actuelle,En revanche, dans la solution proposée ci-dessus, l'interrupteur horaire est basé sur la valeur de
|x| + |y|
, qui est représenté par la éclos carré ci-dessous. Il assure que les deux zones se connecte normalement.Aussi, le code ci-dessus n'ont pas de branchement, qui pourrait être plus efficace. Considérer que les opérations telles que
max
etabs
, qui a priori besoins de ramification, souvent, ont consacré des instructions de montage. Pour cette raison, je pense que cette approche est supérieure à une autre solution qui consisterait à fixer MichaelnearlyEqual
en changeant le commutateur dediff < relth
àdiff < eps * relth
, qui serait alors de produire essentiellement le même profil de réponse.Où pour basculer entre relatif et absolu de comparaison?
Le basculer entre ces deux modes est faite autour de
relth
, qui est considéré commeFLT_MIN
dans la accepté de répondre. Ce choix signifie que la représentation defloat32
est ce qui limite la précision de nos nombres en virgule flottante.Ce n'est pas toujours logique. Par exemple, si les numéros de comparer les résultats d'une soustraction, peut-être quelque chose dans la gamme de
FLT_EPSILON
a plus de sens. S'ils sont carrés des racines de la soustrait des nombres, le numérique imprécision pourrait être encore plus élevé.Il est plutôt évident lorsque l'on considère les comparant à virgule flottante avec
0
. Ici, un parent, une comparaison sera un échec, car|x - 0| /(|x| + 0) = 1
. De sorte que la comparaison doit basculer en mode absolu quandx
est de l'ordre de l'imprécision de votre calcul -- et il est rarement aussi bas queFLT_MIN
.C'est la raison de l'introduction de la
relth
paramètre ci-dessus.Aussi, en ne multipliant
relth
avecepsilon
, l'interprétation de ce paramètre est simple et en adéquation avec le niveau de précision numérique que nous attendons de ces chiffres.Mathématique grondement
(ici surtout pour mon propre plaisir)
Plus généralement, je pense qu'un bon comportement de virgule flottante opérateur de comparaison
=~
doit avoir certaines propriétés de base.Suivantes sont assez évidentes:
a =~ a
a =~ b
impliqueb =~ a
a =~ b
implique-a =~ -b
(Nous n'avons pas
a =~ b
etb =~ c
impliquea =~ c
,=~
n'est pas une relation d'équivalence).Je voudrais ajouter les propriétés suivantes qui sont plus spécifiques à virgule flottante comparaisons
a < b < c
, puisa =~ c
impliquea =~ b
(plus proche des valeurs devraient également être égaux)a, b, m >= 0
puisa =~ b
impliquea + m =~ b + m
(plus les valeurs avec la même différence devrait également être égaux)0 <= λ < 1
puisa =~ b
impliqueλa =~ λb
(peut-être moins évidente à l'argument pour).Ces propriétés donnent déjà de fortes contraintes sur possible la quasi-égalité des fonctions. La fonction proposée ci-dessus, les vérifie. Peut-être un ou plusieurs évidente propriétés sont manquantes.
Quand on pense à
=~
comme une famille de l'égalité relation=~[Ɛ,t]
paramétré parƐ
etrelth
, on pourrait également ajouterƐ1 < Ɛ2
puisa =~[Ɛ1,t] b
impliquea =~[Ɛ2,t] b
(égalité pour une tolérance implique l'égalité à une plus grande tolérance)t1 < t2
puisa =~[Ɛ,t1] b
impliquea =~[Ɛ,t2] b
(égalité pour une imprécision implique l'égalité à une plus grande imprécision)La solution proposée permet également de vérifier ces.
Idée que j'ai eu pour floating point de comparaison dans swift
Adaptation à PHP de Michael Borgwardt & bosonix réponse:
J'ai essayé d'écrire une égalité de la fonction avec les commentaires ci-dessus à l'esprit. Voici ce que j'ai trouvé:
Edit: Changement de Mathématiques.Max(a, b) pour les Mathématiques.Max(Matt.Abs(a), les Maths.Abs(b))
Pensées? J'ai encore besoin de travailler sur une supérieure et une inférieure ainsi.
epsilon
devrait êtreMath.abs(Math.Max(a, b)) * Double.Epsilon;
, ou il sera toujours plus petit quediff
pour le négatifa
etb
. Et je pense que votreepsilon
est trop petite, la fonction peut ne pas retourner quelque chose de différent de la==
de l'opérateur. Plus quea < b && !fpEqual(a,b)
.Vous devriez vous demander pourquoi vous comparez les chiffres. Si vous savez le but de la comparaison, alors vous devez également savoir la précision requise de vos numéros. C'est différent dans chaque situation et de chaque contexte d'application. Mais dans presque tous les cas pratiques, il est nécessaire absolue précision. Il n'est que très rarement qu'une précision relative est applicable.
Pour donner un exemple: si votre but est de dessiner un graphique sur l'écran, alors vous avez probablement envie de valeurs à virgule flottante de comparer égaux s'ils correspondent à la même pixel sur l'écran. Si la taille de votre écran est de 1000 pixels, et vos numéros sont dans le 1e6 plage, alors il est probable que vous voudrez 100 à comparer égal à 200.
Compte tenu de la nécessaire précision absolue, alors l'algorithme devient:
Vous devez prendre en compte que l'erreur de troncation est relatif. Deux numéros sont à peu près identiques si leur différence est à peu près aussi grand que leur ulp (de l'Unité à la dernière place).
Toutefois, si vous ne les calculs en virgule flottante, votre risque d'erreur augmente avec chaque opération (esp. attention avec les soustractions!), si votre tolérance à l'erreur doit augmenter en conséquence.
Conseillé de le faire est d'utiliser quelques petits "epsilon" de la valeur (choisi en fonction de votre application, probablement), et d'envisager des flotteurs qui sont dans l'epsilon de l'autre pour être égaux. par exemple, quelque chose comme
Une réponse plus complète est compliqué, car à virgule flottante d'erreur est extrêmement subtil et déroutant à la raison. Si vous vous souciez vraiment de l'égalité dans tous les sens précis, vous êtes probablement à la recherche d'une solution qui n'implique pas de virgule flottante.
if ((a - b) < EPSILON/a && (b - a) < EPSILON/a)
c
, car une fois que votre nombre est assez grand, l'EPSILON sera plus petite que la précision machine dec
. E. g. supposons quec = 1E+22; d=c/3; e=d+d+d;
. Puise-c
peut ainsi être considérablement plus grand que 1.double a = pow(8,20); double b = a/7; double c = b+b+b+b+b+b+b; std::cout<<std::scientific<<a-c;
(a et c pas égaux selon pnt et nelhage), oudouble a = pow(10,-14); double b = a/2; std::cout<<std::scientific<<a-b;
(a et b égaux selon pnt et nelhage)La meilleure façon de comparer doubles pour l'égalité/inégalité est en prenant la valeur absolue de leur différence, et en le comparant à un assez petit (en fonction de votre contexte) de la valeur.