Ich möchte zwei Floats in PHP vergleichen, wie in diesem Beispielcode:
$a = 0.17;
$b = 1 - 0.83; //0.17
if($a == $b ){
echo 'a and b are same';
}
else {
echo 'a and b are not same';
}
In diesem Code wird das Ergebnis der else
Bedingung anstelle der if
Bedingung zurückgegeben, obwohl $a
und $b
dasselbe sind. Gibt es eine spezielle Möglichkeit, Floats in PHP zu handhaben / zu vergleichen?
Wenn ja, helfen Sie mir bitte, dieses Problem zu lösen.
Oder gibt es ein Problem mit meiner Serverkonfiguration?
php
floating-point
Santosh Sonarikar
quelle
quelle
a and b are same
. Ist das Ihr vollständiger Code?floating-point
Tag-Beschreibung gelesen ? stackoverflow.com/tags/floating-point/info Dies ist ein Verhalten, auf das Sie wahrscheinlich in jeder Programmiersprache stoßen würden, wenn Sie Gleitkommazahlen verwenden. Siehe z. B. stackoverflow.com/questions/588004/is-javascripts-math-brokenAntworten:
Wenn Sie es so machen, sollten sie gleich sein. Beachten Sie jedoch, dass ein Merkmal von Gleitkommawerten darin besteht, dass Berechnungen, die zum gleichen Wert zu führen scheinen , nicht tatsächlich identisch sein müssen. Wenn
$a
es sich also um ein Literal handelt.17
und$b
durch eine Berechnung dort ankommt, kann es durchaus sein, dass sie unterschiedlich sind, obwohl beide den gleichen Wert anzeigen.Normalerweise vergleichen Sie niemals Gleitkommawerte für eine solche Gleichheit. Sie müssen einen kleinsten akzeptablen Unterschied verwenden:
Sowas in der Art.
quelle
abs($a-$b)
>abs(($a-$b)/$b)
0.10000000000000000555111512312578270211815834045410156
normalerweise sinnlos und sie würden es vorziehen,0.1
stattdessen. Und schreiben Sie eine Nummer, damit sie genauso wieder gelesen werden kann. Wie Sie sehen, ist es nicht so eindeutig, wie Sie es sich vorstellen. Und für die Aufzeichnung möchten Sie immer noch Gleitkommazahlen vergleichen, wie ich gezeigt habe, weil Sie zu$a
und$b
über verschiedene Berechnungen gelangen können, die sie unterschiedlich machen können.a=b=0
und wenna
der kleinstmögliche keiner Null positive Wert ist undb
ist der kleinste mögliche nicht Null negativer Wert ist , wird der Test falsch ausfallen. Einige gute Informationen hier: float-point-gui.de/errors/comparison$b
? das PHP - Handbuch tat nurif(abs($a-$b) < $epsilon)
das MySQL - Handbuch haben auch das gleicheHAVING ABS(a - b) <= 0.0001
$a == $b == 0
, aber es ist bereits viel allgemeiner als ein absoluter Fehler. Wenn$a
und$b
in Millionenhöhe, dannEPSILON
müssten Sie ganz anders sein als wenn$a
und$b
irgendwo in der Nähe sind0
. Siehe Doms Link oben für eine bessere Diskussion über dies.Lesen Sie zuerst die rote Warnung im Handbuch . Sie dürfen Floats niemals auf Gleichheit vergleichen. Sie sollten die Epsilon-Technik verwenden.
Beispielsweise:
Dabei
PHP_FLOAT_EPSILON
ist die Konstante eine sehr kleine Zahl (Sie müssen sie in alten PHP-Versionen vor 7.2 definieren).quelle
EPSILON
hier ist eine beliebige benutzerdefinierte Konstante. PHP verfügt nicht über eine integrierte Konstante, die die spezifische Idee einer Architektur von epsilon darstellt. (Siehe auchget_defined_constants
.)PHP_FLOAT_EPSILON
Kleinste darstellbare positive Zahl x, so dass x + 1.0! = 1.0. Verfügbar ab PHP 7.2.0.echo $a - $b; /* 5.6843418860808E-14 */ echo PHP_FLOAT_EPSILON; /* 2.2204460492503E-16 */
Die Frage ist, wie Sie "gleich" für Ihre Anwendung definieren möchten, wie nahe die Zahlen sein sollten, um als gleich zu gelten.Oder versuchen Sie, bc mathematische Funktionen zu verwenden:
Ergebnis:
quelle
bccomp()
nimmt Zeichenfolgen als Argumente. Wie auch immer, Sie könnten esPHP_FLOAT_DIG
für das Skalierungsargument verwenden.Seien Sie, wie bereits erwähnt, sehr vorsichtig, wenn Sie Gleitkomma-Vergleiche (ob gleich, größer als oder kleiner als) in PHP durchführen. Wenn Sie jedoch immer nur an einigen wichtigen Stellen interessiert sind, können Sie Folgendes tun:
Die Verwendung der Rundung auf 2 Dezimalstellen (oder 3 oder 4) führt zum erwarteten Ergebnis.
quelle
loose_float_compare
damit klar ist, was los ist.bccomp($a, $b, 2)
ist Ihrer Lösung überlegen. In diesem Beispiel ist die 2 die Genauigkeit. Sie können eine beliebige Anzahl von Gleitkommazahlen festlegen, die Sie vergleichen möchten.Es wäre besser, einen nativen PHP-Vergleich zu verwenden :
quelle
Wenn Sie Gleitkommawerte zum Vergleich mit Gleichheit haben, können Sie das Risiko einer internen Rundungsstrategie des Betriebssystems, der Sprache, des Prozessors usw. auf einfache Weise vermeiden , indem Sie die Zeichenfolgendarstellung der Werte vergleichen.
Sie können eine der folgenden Methoden verwenden, um das gewünschte Ergebnis zu erzielen: https://3v4l.org/rUrEq
String Type Casting
String-Verkettung
strval Funktion
String-Darstellungen sind bei der Überprüfung der Gleichheit viel weniger pingelig als Floats.
quelle
(string)
Cast-Operation als Referenz ausgeführt wird und die ursprüngliche Deklaration geändert wird. Wenn ja, ist das nicht der Fall. 3v4l.org/CraasWenn Sie eine kleine, endliche Anzahl von Dezimalstellen haben, die akzeptabel sind, funktioniert Folgendes gut (wenn auch mit einer langsameren Leistung als die Epsilon-Lösung):
quelle
Dies funktioniert bei mir unter PHP 5.3.27.
quelle
Für PHP 7.2 können Sie mit PHP_FLOAT_EPSILON ( http://php.net/manual/en/reserved.constants.php ) arbeiten:
quelle
==
und!=
aber nicht>
,>=
,<
,<=
Wenn Sie es einfach so schreiben, wird es wahrscheinlich funktionieren, also stelle ich mir vor, Sie haben es für die Frage vereinfacht. (Und die Frage einfach und präzise zu halten, ist normalerweise eine sehr gute Sache.)
In diesem Fall stelle ich mir jedoch vor, dass ein Ergebnis eine Berechnung und ein Ergebnis eine Konstante ist.
Dies verstößt gegen eine Grundregel der Gleitkommaprogrammierung: Führen Sie niemals Gleichheitsvergleiche durch.
Die Gründe dafür sind etwas subtil 1, aber es ist wichtig, sich daran zu erinnern, dass sie normalerweise nicht funktionieren (außer ironischerweise für ganzzahlige Werte) und dass die Alternative ein unscharfer Vergleich im Sinne von:
1. Eines der Hauptprobleme ist die Art und Weise, wie wir Zahlen in Programme schreiben. Wir schreiben sie als Dezimalzeichenfolgen, und daher haben die meisten von uns geschriebenen Brüche keine exakten Maschinendarstellungen. Sie haben keine exakten endlichen Formen, weil sie sich binär wiederholen. Jeder Maschinenbruch ist eine rationale Zahl der Form x / 2 n . Jetzt sind die Konstanten dezimal und jede Dezimalkonstante ist eine rationale Zahl der Form x / (2 n * 5 m ). Die 5- m- Zahlen sind ungerade, daher gibt es für keinen von ihnen einen 2- n- Faktor. Nur wenn m == 0 ist, gibt es eine endliche Darstellung sowohl in der binären als auch in der dezimalen Erweiterung des Bruchs. 1,25 ist also genau, weil es 5 / (2 2 * 5 0 ist) aber 0.1 ist nicht, weil es 1 / (2 0 * 5 1 ) ist. Tatsächlich sind in der Serie 1.01 .. 1.99 nur 3 der Zahlen genau darstellbar: 1.25, 1.50 und 1.75.
quelle
round($float, 3) == round($other, 3)
Hier ist die Lösung zum Vergleichen von Gleitkomma- oder Dezimalzahlen
Wirf eine
decimal
Variable aufstring
und es wird dir gut gehen.quelle
Der Vergleich von Floats auf Gleichheit hat einen naiven O (n) -Algorithmus.
Sie müssen jeden Gleitkommawert in eine Zeichenfolge konvertieren und dann jede Ziffer beginnend von der linken Seite der Zeichenfolgendarstellung jedes Gleitkommas mit ganzzahligen Vergleichsoperatoren vergleichen. PHP überträgt die Ziffer an jeder Indexposition vor dem Vergleich automatisch auf eine Ganzzahl. Die erste Ziffer, die größer als die andere ist, unterbricht die Schleife und deklariert den Float, zu dem sie gehört, als die größere der beiden. Im Durchschnitt wird es 1/2 * n Vergleiche geben. Für gleich große Floats gibt es n Vergleiche. Dies ist das Worst-Case-Szenario für den Algorithmus. Das beste Szenario ist, dass die erste Ziffer jedes Floats unterschiedlich ist und nur einen Vergleich verursacht.
Sie können INTEGER COMPARISON OPERATORS nicht für Float-Rohwerte verwenden, um nützliche Ergebnisse zu erzielen. Die Ergebnisse solcher Operationen haben keine Bedeutung, da Sie keine ganzen Zahlen vergleichen. Sie verletzen die Domäne jedes Operators, was zu bedeutungslosen Ergebnissen führt. Dies gilt auch für den Delta-Vergleich.
Verwenden Sie Ganzzahlvergleichsoperatoren für das, wofür sie entwickelt wurden: Vergleichen von Ganzzahlen.
VEREINFACHTE LÖSUNG:
quelle
2019
TL; DR
Verwenden Sie meine Funktion unten wie folgt
if(cmpFloats($a, '==', $b)) { ... }
cmpFloats($a, '<=', $b)
vs.bccomp($a, $b) <= -1
Zusammenfassung
Ich werde das Geheimnis enthüllen.
Wenn Sie also Folgendes versuchen, ist es gleich:
Wie erhalte ich den tatsächlichen Wert von float?
Wie können Sie vergleichen?
==
und vergleichen und!=
sie in Strings typisieren können, sollte dies perfekt funktionieren:Geben Sie cast mit string ein :
Oder typisiert mit
number_format()
:Warnung:
Vermeiden Sie Lösungen, bei denen Floats mathematisch manipuliert (multipliziert, dividiert usw.) und dann verglichen werden. Meistens lösen sie einige Probleme und führen andere Probleme ein.
Vorgeschlagene Lösung
Ich habe eine reine PHP-Funktion erstellt (keine Abhängigkeiten / Bibliotheken / Erweiterungen erforderlich). Überprüft und vergleicht jede Ziffer als Zeichenfolge. Funktioniert auch mit negativen Zahlen.
quelle
Funktion von @evilReiko haben einige Fehler wie diese:
In meiner Funktion habe ich diese Fehler behoben, aber in einigen Fällen gibt diese Funktion falsche Antworten zurück:
Feste Funktion zum Vergleichen von Floats
Antwort auf Ihre Frage
quelle
Hier ist eine nützliche Klasse aus meiner persönlichen Bibliothek für den Umgang mit Gleitkommazahlen. Sie können es nach Ihren Wünschen tweeken und eine beliebige Lösung in die Klassenmethoden einfügen :-).
quelle
Einfache Antwort:
quelle