PHP Dezimalzahlen auf Gleichheit überprüfen

Technik
PHP Dezimalzahlen auf Gleichheit überprüfen

PHP benimmt sich auf den ersten Blick etwas seltsam, wenn es um Dezimalzahlen geht. Vergleicht man zwei Variablen, die beide den selben Zahlenwert haben, kann PHP trotz Gleichheit ein false zurückgeben.

Der erste Kommentar aus der PHP-Dokumentation zu Floating point numbers illustriert ein gutes Beispiel.

$x = 8 - 6.4; // which is equal to 1.6
$y = 1.6;
var_dump($x == $y); // is not true

Wie man hier sieht, werden $x und $y verglichen. Beide müssten den Wert 1.6 haben. Da $x jedoch aus einer Berechnung kommt, interpretiert PHP den Wert etwas anders. Die binäre Art zu rechnen kann die Dezimalzahl nur annähernd genau berechnen und tatsächlich enthält $x in diesem Beispiel den Wert 1.5999999999999996447. Das kann man auch selber nachtesten, indem man den php.ini-Wert precision auf z.B. 20 erhöht und sich die Variable mittels var_dump ausgeben lässt.

Leider besitzt PHP auch keinen Dezimal-Datentyp, womit für Dezimalzahlen nur der float-Typ übrig bleibt.

Floating point numbers vergleichen

Aber wie kann man die Zahlen nun dennoch vergleichen? Hier gibt es zwei Möglichkeiten. Entweder man rundet oder man verwendet die PHP Erweiterung BC Math.

Runden

if(round((float)$x, 2) == round((float)$y, 2))
	// do something

BC Math bccomp

if(bccomp($x, $y, 2) == 0)
	// do something

Bei beiden Methoden muss man sich aber über die Anzahl der Nachkommastellen Gedanken machen. Handelt es sich um Preise, sollten es wohl stets 2 Kommastellen sein. Bei anderen Zahlen muss man diesen Wert aber entsprechend anpassen.

Alternativ kann man auch bereits bei der Berechnung selbst Vorsichtsmaßnahmen treffen, damit PHP die Zahlen von vorne herein genau berechnet. Auch hier muss man die BC Math Erweiterung verwenden. Anstatt einer Summe mit dem + Operator ist dann eben die Funktion bcadd zu benutzen.

$x = '0.1';
$y = '0.2';
echo bcadd($x, $y); // prints 0.3

Weitere wertvolle Informationen zum Nachlesen gibt es auf floating-point-gui.de.

Permalink: https://to.ptmr.io/1EnfAJS