2016-09-09 17 views
4

Die Frage, wie Gleitkommazahlen zu vergleichen sind has been answered here. Diese Frage ist insofern anders, als ich nach den Formeln frage. Die beiden höchsten gestimmt Antworten haben eine etwas andere Lösung für das Problem:Formeln zum Vergleich der Float-Gleichheit in PHP

if (abs(($a-$b)/$b) < $epsilon) { … } 

und

if (abs($a-$b) < $epsilon) { … } 

Warum wird die erste Antwort enthält die Teilung? Führt es nicht zu ungenauen Ergebnissen? Zum Beispiel (unter Verwendung von einfachen Zahlen), lassen Sie beide $ a und $ b gleich 0,01, und nehmen an, dass $ a - $ b 0,0001 ergibt, mit einem $ epsilon von 0,001.

((((0.01 - 0.01) == 0.0001)/0.01 == 0.01) < 0.001) : false 

während

(((0.01 - 0.01) == 0.0001) < 0.001) : true 

kann Meine Mathe ein wenig rostig, aber was bin ich dabei?

Wann sollte ich die eine Formel über die andere verwenden?

+1

@luweiqi kein Duplikat, verschiedene Fragen. Ich habe sogar das vorgeschlagene Duplikat verlinkt. – Twifty

+0

Yup, Ihre aktualisierte Frage ist klarer, ich habe meine enge Abstimmung zurückgezogen. Prost! – Panda

+0

Related: http://stackoverflow.com/q/328475/3990767 – SOFe

Antwort

3

Dies ermöglicht wahrscheinlich die Prüfung von Epsilon mit einem relativen Fehler anstelle eines absoluten Fehlers.

Vergleichen Sie diese beiden Fälle:

function areEqual(float $a, float $b) : bool { 
    return abs(($a - $b)/$b) < 0.00001; 
} 
areEqual(10000, 10000.01); 
areEqual(0.0000001, 0); 

Tatsache über die Beispielwerte oben: Unsere epsilon hier ist 0.00001 für Bequemlichkeit ‐ die kleinste epsilon möglich ist, viel kleiner als diese Werte ohnehin, also lassen Sie uns diese Tatsache ignorieren . Unser Algorithmus geht davon aus, dass $a und $b beide ähnlich sind, also ist es egal, ob wir dividieren durch $a oder $b. Eigentlich sollte 10000 viel größer als das sein (ein sehr großer Exponent), und 0.0000001 kann viel kleiner sein, aber der Bequemlichkeit halber angenommen, dies sind die Werte, die Probleme verursachen können.

Jetzt können Sie bereits den Unterschied sehen.

Für die großen Zahlen: Wenn die verglichenen Floats extrem groß sind, kann Epsilon zu klein sein. Der Float intern kann nur eine bestimmte Anzahl von Ziffern für die Genauigkeit speichern, während der Exponent viel größer sein kann. Als Ergebnis würde die Quelle des Gleitkommafehlers, d. H. Die Endziffern der Gleitkommazahlen, an einer Stelle erscheinen, die höher als die Einheitsziffern sein kann.Mit anderen Worten, für extrem große Schwimmer kann der absolute Fehler größer als 1 sein, viel weniger unser epsilon 0.00001.

Für die kleinen Zahlen: Dies ist noch offensichtlicher. Beide Zahlen sind bereits kleiner als das Epsilon. Selbst wenn Sie sie mit 0 vergleichen, während der relative Fehler unendlich groß ist, denken Sie immer noch, dass sie gleich sind. Für diesen Fall multiplizieren Sie entweder beide Operanden oder Sie verringern das Epsilon. Sie sind eigentlich die gleichen, aber in Bezug auf die Implementierung ist es bequemer, die Differenz mit einem der Operanden zu teilen, die für kleine Zahlen (/ 0.0001 ist gleich * 10000) oder teilen für große Zahlen (/ 10000 während der Unterschied ist hoffentlich viel kleiner als 10000)

Es gibt einen anderen Namen für diese Überprüfung. Während abs($a - $b) der absolute Fehler genannt wird, verwenden wir normalerweise den relativen Fehler, der ein absoluter Fehler ist. Da die Werte auch negativ sein können, verwenden wir abs das Ganze ($a - $b)/$b stattdessen. Unser "Epsilon", 0.00001, bedeutet in diesem Fall, dass unser relativer Toleranzfehler 0.00001 ist, d. H. 0,001% Fehler.


Denken Sie daran, dass dies noch nicht absolut sicher ist. Nach zahlreichen Transformationen in Ihrem Programm können Sie zum Beispiel Ihre Zahlen mit einigen großen Zahlen addieren/multiplizieren, dann wieder subtrahieren und den unreinen Fehler in den großen Zahlen für den Menschen noch vernachlässigbar machen, aber bemerkenswert für Ihren Epsilon-Wert. Denken Sie deshalb immer zweimal darüber nach, bevor Sie einen Epsilon-Wert oder einen Float-Vergleichsalgorithmus wählen.

Vermeiden Sie es, große Zahlen mit kleinen Zahlen zu addieren, zu subtrahieren oder zu multiplizieren. Sie erhöhen die Wahrscheinlichkeit von Fehlern. Denken Sie bei der Entwicklung (vor allem Vereinfachung) Ihrer Algorithmen immer daran, dass es sich um einen Fehler in Ihren Floats handeln könnte. Dies kann die Arbeitsbelastung auf ein dummes Ausmaß erhöhen, aber solange Sie sich dessen bewusst sind, erspart Ihnen diese Art von Sorge manchmal, aus den Teams rausgeschmissen zu werden.

+0

Dieser letzte Absatz traf nach Hause. Ich mache einen einfachen GPS-Koordinatenvergleich, aber ich habe einige der Algorithmen vergessen, die ich verwende, um sie zu generieren. In meinem Fall ist es mir nur wichtig, dass das Endergebnis nur wenige Meter voneinander entfernt ist. Daher denke ich, dass die Verwendung eines absoluten Fehlers ausreichend ist. Etwas, das man für die Zukunft im Auge behalten sollte. – Twifty

+0

@Twifty danke. Bitte akzeptieren Sie die Antwort, wenn Sie es hilfreich finden. – SOFe

0

Es ist eine Frage der Genauigkeit gegenüber der Genauigkeit. Wenn du die Division nicht machst, betrachtest du Präzision, so dass du deinen Standard kennst und gut bis zur n. Dezimalstelle (oder was immer du wählst) gut genug ist. Wenn Sie die Teilung machen, sprechen Sie Genauigkeit (denken Sie Prozentfehler), also ist eine Unze schrecklich, wenn Sie einen Goldohrring verkaufen und fein, wenn Sie sich wiegen.

0

Die Division berücksichtigt die Tatsache, dass Gleitkommazahlen einen Exponenten haben. Dies bedeutet, dass der kleinste Unterschied zwischen zwei aufeinanderfolgenden Zahlen größer wird, wenn die Zahlen selbst größer werden.

Zum Beispiel 1e-300 und 2e-300 zwei verschiedene Zahlen (für einen IEE 754 64-Bit-Typen), aber 1e300 und 1e300 + 1e-300 sind gleich.