Genaue Dezimalzahlen in PHP

Die meisten Webanwendungen verarbeiten zu einem bestimmten Zeitpunkt Dezimalzahlen – Währung, Koordinaten, Messungen, wissenschaftliche Arithmetik -, aber PHP bietet keine standardmäßige oder klare Empfehlung für den Umgang mit Zahlen, wenn Genauigkeit wichtig ist. Es gibt natürlich Fälle, in denen Genauigkeit nicht wichtig oder zumindest nicht so wichtig wie Leistung ist.

PHPs float ist ein IEEE 754–1985 double unter der e -Haube , ist eine binäre Gleitkommazahl und kann daher bestimmte Zahlen wie 0,2 nicht darstellen. Dies liegt daran, dass der Wert normalerweise als Ganzzahl multipliziert mit 2 hoch einem Exponenten gespeichert wird: a * 2 ^ b = c , wobei a und b ganze Zahlen sind. Wenn es für a und b keine ganzzahligen Lösungen gibt, ist die resultierende Zahl eine Annäherung. Dies führt zu einem kontraintuitiven Verhalten:

Das Zahlensystem, das wir in der Schule lernen und in unserem täglichen Leben verwenden, ist natürlich dezimal . Wenn wir float verwenden, verwenden wir einen binären Typ, um eine Dezimalzahl darzustellen, da wir mit Hardware sehr schnell binäre Berechnungen durchführen können. Um eine Potenz von 2 zu berechnen, müssen wir nur ein einzelnes Bit setzen:

Um Dezimalzahlen genau darzustellen, müssten wir eine Potenz von 10 anstelle von 2 verwenden. Leider unterstützen die meisten Architekturen keine native Basis-10-Arithmetik, daher müssten wir berechnen diese Macht.

Der Kompromiss hier ist die Leistung für die Genauigkeit.

Wenn Genauigkeit wichtig ist, ist dieser Kompromiss einfach, da Sie mehr CPU-Leistung zuweisen können, um den Leistungseinbruch auszugleichen, aber die zugrunde liegenden Architekturbeschränkungen nicht ändern können. Die meisten PHP-Anwendungen haben ohnehin einen Engpass im Dateisystem oder auf Netzwerkebene. ¯ \ _ (ツ) _ / ¯

Wie können wir in PHP eine genaue Dezimaldarstellung erreichen?

Es gibt einige existierende Lösungen, die schon lange Teil von PHP sind: bcmath und gmp. Diese Erweiterungen bieten eine Schnittstelle zum Erstellen und Bearbeiten sehr großer, genauer Zahlen.

Weitere Informationen hierzu finden Sie im Gleitkomma-Spickzettel für PHP .

Ein gängiger Transporttyp ist string (was bcmath akzeptiert und zurückgibt), und es gibt einige nette Abstraktionen, die alles für Sie erledigen. Die Suche nach “Dezimal” in Packagist zeigt eine Handvoll Ergebnisse, aber hier gibt es keinen eindeutigen Gewinner. Dies sagt mir, dass die meisten Anwendungen, die genaue Zahlen benötigen, diese Erweiterungen entweder direkt oder ihre eigenen Abstraktionen verwenden.

Ich möchte einen neuen Standard für den Umgang mit Dezimalzahlen in PHP vorschlagen, der nicht der einzige Weg sein muss, sondern zumindest eine Grundlage für die Best-Practice-Empfehlung.

Einführung der Erweiterung dezimal . ?

Diese Erweiterung bietet Unterstützung für korrekt gerundete Dezimal-Gleitkomma-Arithmetik mit beliebiger Genauigkeit. Anwendungen, die auf genauen Zahlen basieren, können die Klasse Decimal anstelle von float oder string.

verwenden

Ein Analogon für SQLs DECIMAL

Wenn Sie eine Anwendung mit PHP erstellen, besteht eine gute Chance, dass Sie unter anderem auch MySQL oder PostgreSQL verwenden. Die Schnittstelle der Dezimalerweiterung wurde unter Berücksichtigung des Typs DECIMAL entwickelt. Ziel war es, das Transportieren und Übersetzen von Dezimalwerten zwischen der Datenbank und der Anwendung zu vereinfachen.

Um dies zu veranschaulichen, verwenden wir ein Beispiel, in dem ein Dollarwert gespeichert werden soll. Wenn wir 20 Stellen links vom Dezimalpunkt und 4 Stellen rechts (für gebrochene Cent) unterstützen möchten, würden wir DECIMAL (24,4) für die Spalte verwenden und 24 als Genauigkeit für die Dezimalstelle. PDO würde dann den Wert als Zeichenfolge bereitstellen und akzeptieren, wodurch die Verwendung von float vollständig vermieden wird. Zum Beispiel:

Ich möchte keine Erweiterung als Abhängigkeit einfügen.

Dies war bisher die häufigste Reaktion auf die Empfehlung der Dezimalerweiterung als Lösung. Obwohl ich die Vorsicht gegenüber nicht standardmäßigen Erweiterungen verstehe und respektiere, wird es immer einfacher, diese als Abhängigkeiten zu verwalten, wie Sie es bei jeder anderen Abhängigkeit tun würden: Composer, gepaart mit containergestützten Umgebungen.

Erfordert “ php-decimal / php-decimal” als Abhängigkeit, die indirekt eine Abhängigkeit von der Erweiterung hinzufügt und Stubs für die automatische Vervollständigung bereitstellt.

Composer installiert keine Erweiterungen , daher sind einige zusätzliche Schritte erforderlich.

Weitere Informationen zu diesem und dem Rest des Projekts finden Sie in der Dokumentation und API unter php-decimal.io