In der schnelllebigen Finanzwelt ist Präzision alles. Es ist immer besser, einen Millionenverlust aufgrund eines Rundungs-/Präzisionsfehlers zu vermeiden.
Der Ausgangspunkt dieses Artikels ist die Erkenntnis, dass Geld nicht die durchschnittliche Grundzahl ist, mit der Sie die Äpfel in Ihrem Warenkorb zählen können. Was erhalten Sie, wenn Sie versuchen, 10 € mit 10 $ zu multiplizieren? Schwierig, oder? Haben Sie schon einmal wundersame 1.546 $ in der Tasche einer alten Jacke gefunden? Ja, ich weiß, das ist auch nicht wirklich möglich. Diese albernen Beispiele sollen die Tatsache veranschaulichen, dass Geld seine eigenen Regeln hat und nicht nur durch eine einfache Zahl modelliert werden kann. Ich versichere Ihnen, ich bin nicht der Erste, der das erkannt hat (und vielleicht haben Sie es schon viel vor mir erkannt). Im Jahr 2002 schlug der Programmierer Martin Fowler in den Patterns of Enterprise Application Architecture eine Möglichkeit vor, Geld mit spezifischen Attributen und Operandenregeln darzustellen. Für ihn waren die beiden minimal brauchbaren Attribute, die für einen Gelddatentyp benötigt wurden:
amount currency
Diese wirklich grundlegende Darstellung wird unser Ausgangspunkt für die Konstruktion eines einfachen, aber robusten Geldmodells sein.
Ein Geldbetrag ist definitiv eine bestimmte Zahl: Sie hat eine feste Genauigkeit (auch hier können Sie nicht 4,376 $ in Ihrer Tasche haben). Sie müssen eine Art der Darstellung wählen, die Ihnen hilft, diese Einschränkung zu respektieren.
Spoiler-Alarm, das ist definitiv keine gute Idee, wenn Sie nicht möchten, dass ein paar Cent (wenn es keine Dollars sind) in der dunklen Welt der Gleitkommazahlendarstellung verschwinden.
Wenn Sie Erfahrung mit dem Codieren in JavaScript haben, wissen Sie, dass selbst die einfachste Berechnung zu einem Präzisionsfehler führen kann, den Sie zunächst nicht erwarten würden. Das offensichtlichste und bekannteste Beispiel, das dieses Phänomen verdeutlicht, ist:
0.1 0.2 !== 0.3 // true 0.1 0.2 // 0.30000000000000004
Wenn Sie dieses Beispiel nicht vollständig überzeugt, empfehle ich Ihnen, einen Blick auf diesen Artikel zu werfen, der etwas tiefer in alle problematischen Berechnungsergebnisse eintaucht, die bei der Arbeit mit dem nativen JavaScript-Zahlentyp auftreten können…
Diese leichte Abweichung in den Ergebnissen mag für Sie harmlos erscheinen (mit einer Größenordnung von etwa ~ 10^-16), in einer kritischen Finanzanwendung kann sich ein solcher Fehler jedoch schnell ausbreiten. Erwägen Sie den Transfer von Geldern zwischen Tausenden von Konten, wobei jede Transaktion ähnliche Berechnungen erfordert. Die geringfügigen Ungenauigkeiten summieren sich und bevor Sie es merken, sind Ihre Finanzberichte um Tausende von Dollar falsch. Und ehrlich gesagt sind wir uns alle einig, dass Fehler nicht erlaubt sind, wenn es um Geld geht: sowohl rechtlich als auch zum Aufbau einer vertrauensvollen Beziehung zu Ihren Kunden.
Die erste Frage, die ich mir stellte, als ich in einem meiner Projekte auf das Problem stieß, war Warum ? Ich habe herausgefunden, dass die Ursache des Problems nicht JavaScript ist und dass diese Ungenauigkeiten auch andere moderne Programmiersprachen (Java, C, Python, …) betreffen.
// In C #includeint main() { double a = 0.1; double b = 0.2; double sum = a b; if (sum == 0.3) { printf("Equal\n"); } else { printf("Not Equal\n"); // This block is executed } return 0; } // > Not equal
// In Java public class doublePrecision { public static void main(String[] args) { double total = 0; total = 5.6; total = 5.8; System.out.println(total); } } // > 11.399999999999
Tatsächlich liegt die Ursache im Standard, den diese Sprachen zur Darstellung von Gleitkommazahlen verwenden: dem Gleitkommaformat mit doppelter (oder einfacher) Genauigkeit, das im IEEE 754-Standard festgelegt ist.
In Javascript entspricht der native Typ Zahl Gleitkommazahlen mit doppelter Genauigkeit, was bedeutet, dass eine Zahl mit 64 Bit codiert und in drei Teile unterteilt wird:
Dann müssen Sie die folgende Formel verwenden, um Ihre Bitdarstellung in einen Dezimalwert umzuwandeln:
Ein Beispiel für die Darstellung von Gleitkommazahlen mit doppelter Genauigkeit, eine Näherung von 1/3 :
0 01111111101 0101010101010101010101010101010101010101010101010101 = (-1)^0 x (1 2^-2 2^-4 2^-6 ... 2^-50 2^-52) x 2^(1021-1023) = 0.333333333333333314829616256247390992939472198486328125 ~ 1/3
Dieses Format ermöglicht es uns, einen großen Wertebereich darzustellen, es kann jedoch nicht jede mögliche Zahl mit absoluter Präzision darstellen (nur zwischen 0 und 1 gibt es unendlich viele Zahlen…). Viele Zahlen lassen sich nicht exakt binär darstellen. Um das erste Beispiel zu wiederholen: Das ist das Problem mit 0,1 und 0,2. Die Doppelkomma-Gleitkomma-Darstellung gibt uns eine Annäherung an diese Werte. Wenn Sie also diese beiden ungenauen Darstellungen addieren, ist das Ergebnis ebenfalls nicht genau.
Da Sie nun völlig davon überzeugt sind, dass der Umgang mit Geldbeträgen mit dem nativen JavaScript-Zahlentyp eine schlechte Idee ist (zumindest hoffe ich, dass Sie anfangen, daran zu zweifeln), lautet die 1-Milliarde-Dollar-Frage wie sollten Sie vorgehen? ? Eine Lösung könnte darin bestehen, einige der leistungsstarken Arithmetikpakete mit fester Genauigkeit zu nutzen, die in JavaScript verfügbar sind. Zum Beispiel Decimal.js (das vom beliebten ORM Prisma zur Darstellung seines Decimal-Datentyps verwendet wird) oder Big.js.
Diese Pakete stellen Ihnen spezielle Datentypen zur Verfügung, die es Ihnen ermöglichen, Berechnungen durchzuführen und dabei die oben erläuterten Präzisionsfehler zu beseitigen.
// Example using Decimal.js const Decimal = require('decimal.js'); const a = new Decimal('0.1'); const b = new Decimal('0.2'); const result = a.plus(b); console.log(result.toString()); // Output: '0.3'
Dieser Ansatz bietet Ihnen einen weiteren Vorteil: Er erweitert den maximal darstellbaren Wert drastisch, was beispielsweise beim Umgang mit Kryptowährungen sehr praktisch sein kann.
Auch wenn es wirklich robust ist, ist es nicht das, was ich am liebsten für die Implementierung in meinen Webanwendungen wähle. Ich finde es einfacher und klarer, die Strategie Stripe anzuwenden, um nur mit ganzzahligen Werten umzugehen.
Wir bei Theodo Fintech legen Wert auf Pragmatismus! Wir lassen uns gerne von den erfolgreichsten Unternehmen der Branche inspirieren. Stripe, das bekannte Milliardenunternehmen, das sich auf Zahlungsdienste spezialisiert hat, hat sich dafür entschieden, Geldbeträge ohne Gleitkommazahlen, sondern mit Ganzzahlen zu verarbeiten. Dazu verwenden sie die kleinste Einheit der Währung, um einen Geldbetrag darzustellen.
// 10 USD are represented by { "amount": 1000, "currency": "USD" }
Ich vermute, dass viele von Ihnen das bereits wissen: Nicht alle Währungen haben die kleinste Einheit derselben Größe. Die meisten davon sind „zweidezimale“ Währungen (EUR, USD, GBP), was bedeutet, dass ihre kleinste Einheit 1/100 der Währung ist. Bei einigen handelt es sich jedoch um „Drei-Dezimal“-Währungen (KWD) oder sogar „Null-Dezimal“-Währungen (JPY). (Weitere Informationen dazu finden Sie, indem Sie dem ISO4217-Standard folgen.) Um diese Unterschiede zu bewältigen, sollten Sie in Ihre Gelddatendarstellung den multiplikativen Faktor integrieren, um einen in der kleinsten Einheit dargestellten Betrag in die entsprechende Währung umzurechnen.
Ich denke, Sie haben es bereits herausgefunden. Sie können entweder mit nativen Zahlen, Paketen mit beliebiger Genauigkeit von Drittanbietern oder Ganzzahlen arbeiten. Berechnungen können (und werden) zu Gleitkommaergebnissen führen, die Sie auf Ihren Geldwert runden müssen endliche Präzision. Ein kurzes Beispiel ist nie zu viel: Nehmen wir an, Sie verarbeiten ganzzahlige Werte und haben einen Kredit über 16.000 $ mit einem wirklich genauen Zinssatz von 8,5413 % abgeschlossen (autsch…). Sie müssen dann 16.000 $ plus einen zusätzlichen Betrag von
zurückerstatten.
1600000 * 0.085413 // amount in cents //Output in cents: 136660.8
Der Knackpunkt besteht darin, den Rundungsprozess von Geldbeträgen nach Berechnungen ordnungsgemäß durchzuführen. Meistens müssen Sie zwischen drei verschiedenen Rundungsarten wählen:
Wenn es um Rundungen geht, gibt es eigentlich keine "Zaubersauce": Sie müssen je nach Situation entscheiden. Ich empfehle Ihnen, immer die Gesetzgebung zu prüfen, wenn Sie sich mit einer neuen Währung und einem neuen Rundungsanwendungsfall befassen (Umrechnung, Geldaufteilung, Zinssätze für Kredite, …). Befolgen Sie die Vorschriften besser sofort, um weitere Probleme zu vermeiden. Wenn es beispielsweise um Umrechnungskurse geht, haben die meisten Währungen Regeln für die erforderliche Genauigkeit und Rundungsregeln festgelegt (Sie können sich hier die Regeln für den EUR-Umrechnungskurs ansehen).
Dieser Artikel erhebt keinen Anspruch auf Vollständigkeit aller vorhandenen Möglichkeiten zur Verarbeitung von Geldbeträgen in JavaScript und ist auch nicht dazu gedacht, Ihnen ein vollständiges/perfektes Gelddatenmodell zu bieten. Ich habe versucht, Ihnen genügend Hinweise und Richtlinien zu geben, um eine Darstellung umzusetzen, die sich als konsistent und belastbar erwiesen hat und die von großen Akteuren der Fintech-Branche gewählt wurde. Ich hoffe, dass Sie in Ihren zukünftigen Projekten Geldbetragsberechnungen durchführen können, ohne einen Cent in Ihrer Tasche zu vergessen (ansonsten vergessen Sie nicht, einen Blick in Ihre alte Jacke zu werfen)!
Haftungsausschluss: Alle bereitgestellten Ressourcen stammen teilweise aus dem Internet. Wenn eine Verletzung Ihres Urheberrechts oder anderer Rechte und Interessen vorliegt, erläutern Sie bitte die detaillierten Gründe und legen Sie einen Nachweis des Urheberrechts oder Ihrer Rechte und Interessen vor und senden Sie ihn dann an die E-Mail-Adresse: [email protected] Wir werden die Angelegenheit so schnell wie möglich für Sie erledigen.
Copyright© 2022 湘ICP备2022001581号-3