[Geldverarbeitung] Wie können Geschäftsberechnungen sicherstellen, dass die Genauigkeit nicht verloren geht?

Projektgesteuertes Lernen und praxiserprobtes Wissen

Vorwort

Viele Systeme erfordern die „Verarbeitung von Geld“, beispielsweise E-Commerce-Systeme, Finanzsysteme, Kassensysteme usw. Solange es etwas mit Geld zu tun hat, muss man es mit 120.000 Energiepunkten behandeln und darf keinen einzigen Fehler machen, sonst wird es eine Katastrophe für das System und die Benutzer.

Es gibt zwei Hauptaspekte für die Genauigkeit garantierter Beträge: Überlauf und Präzision . Überlauf bedeutet, dass genügend Speicherplatz zum Speichern von Daten vorhanden ist und diese nicht gespeichert werden können, wenn die Menge zu groß ist. Genauigkeit bedeutet, dass es bei der Berechnung der Menge keine Abweichungen geben darf und weder mehr noch weniger funktionieren.

float Jeder weiß, wie man das Überlaufproblem löst. Wählen Sie einfach einen numerischen Typ mit einer großen Anzahl von Ziffern, das heißt, verwenden Sie ihn  nicht  double . Das Präzisionsproblem double kann nicht gelöst werden, da Gleitkommazahlen zu Präzisionsverlusten führen.

Lassen Sie uns den Präzisionsverlust intuitiv spüren:

double money = 1.0 - 0.9;

Jeder kennt das Ergebnis dieser Berechnung  0.1, aber das tatsächliche Ergebnis ist  0.09999999999999998. Dieses Phänomen tritt auf, weil die unterste Ebene des Computers binäre Operationen sind und binäre Dezimalzahlen nicht genau darstellen können. Daher müssen bei präzisen Berechnungen wie z. B. kommerziellen Berechnungen andere Datentypen verwendet werden, um sicherzustellen, dass die Genauigkeit nicht verloren geht, und es dürfen keine Gleitkommazahlen verwendet werden.

Als Nächstes erklärt dieser Krebs ausführlich, wie kommerzielle Berechnungen in der tatsächlichen Entwicklung durchgeführt werden, und stellt den gesamten Code und die SQL-Anweisungen auf Github ab (die Adresse befindet sich am Ende des Artikels). Nach dem Klonen kann er ausgeführt werden.

Lösung

Es gibt zwei Datentypen, die die Anforderungen der Geschäftsinformatik erfüllen können: Der erste ist natürlich der speziell für die Geschäftsinformatik entwickelte  Decimal-Typ  und der zweite ist eine Ganzzahl fester Länge .

Dezimal

Bei der Auswahl der Datentypen muss der eine die Datenbank und der andere die Programmiersprache berücksichtigen. Das heißt, welcher Typ wird zum Speichern von Daten in der Datenbank verwendet und welcher Typ wird zum Verarbeiten von Daten im Code verwendet .

Auf Datenbankebene wird dieser Typ natürlich verwendet  decimal , da bei diesem Typ kein Präzisionsverlust auftritt und er sich perfekt für kommerzielle Berechnungen eignet.

decimal Die Syntax  zum Definieren eines Felds wie  sie ist decimal(M,N), M gibt an, wie viele Ziffern gespeichert werden sollen, und N gibt an, wie viele Dezimalstellen gespeichert werden sollen. Angenommen  decimal(20,2), dies bedeutet, dass insgesamt 20 Ziffern gespeichert sind, von denen Dezimalstellen 2 Ziffern belegen.

Wir erstellen eine neue Benutzertabelle mit zwei einfachen Feldern, Primärschlüssel und Saldo:

Hier sind 2 Punkte für die Dezimalstelle reserviert, was bedeutet, dass der Betrag nur in Cent gespeichert wird und die im tatsächlichen Projekt gespeicherte Einheit von den Geschäftsanforderungen abhängt, was alles möglich ist.

Die Datenbankebene ist fertig. Schauen wir uns die Codeebene an. In Java entspricht der Typ der Datenbank  decimal und  java.math.BigDecimalkann natürlich sicherstellen, dass die Genauigkeit absolut korrekt ist.

BigDecimalEs gibt drei Hauptmethoden zum Erstellen :

BigDecimal d1 = new BigDecimal(0.1); // BigDecimal(double val)
BigDecimal d2 = new BigDecimal("0.1"); // BigDecimal(String val)
BigDecimal d3 = BigDecimal.valueOf(0.1); // static BigDecimal valueOf(double val)

Die ersten beiden sind Konstruktoren und die letztere ist eine statische Methode. Alle drei Methoden sind sehr praktisch, aber die erste Methode ist verboten! Schauen Sie sich die Druckergebnisse jedes dieser drei Objekte an, um herauszufinden, warum:

d1: 0.1000000000000000055511151231257827021181583404541015625
d2: 0.1
d3: 0.1

Bei der ersten Methode  double können die über den Konstruktor übergebenen Parameter des Typs den Wert nicht genau ermitteln. Wenn Sie ihn korrekt erstellen möchten  BigDecimal, müssen Sie  double ihn entweder in eine Zeichenfolge konvertieren und dann den Konstruktor aufrufen oder die statische Methode direkt aufrufen. Tatsächlich befindet sich in der statischen Methode auch  double der Konstruktor, der in einen String konvertiert und dann aufgerufen wird:

Wenn der Dezimalwert aus der Datenbank abgefragt oder vom Frontend übergeben wird, werden die Daten genau  BigDecimal einem Objekt zugeordnet, sodass wir uns darüber keine Sorgen machen müssen.

Nachdem wir über die Schöpfung gesprochen haben, sprechen wir über die wichtigsten numerischen Operationen. Operationen sind nichts anderes als Addition, Subtraktion, Multiplikation und Division, die  BigDecimal entsprechende Methoden bereitstellen:

BigDecimal add(BigDecimal); // 加
BigDecimal subtract(BigDecimal); // 减
BigDecimal multiply(BigDecimal); // 乘
BigDecimal divide(BigDecimal); // 除

BigDecimal Es handelt sich um ein unveränderliches Objekt. Dies bedeutet, dass diese Vorgänge den Wert des ursprünglichen Objekts nicht ändern und nach Ausführung der Methode nur ein neues Objekt zurückgegeben werden. Wenn Sie den ursprünglichen Wert nach der Operation aktualisieren möchten, können Sie ihn nur neu zuweisen:

d1 = d1.subtract(d2);

Es gibt keinen Beweis. Lassen Sie uns überprüfen, ob die Genauigkeit verloren geht:

BigDecimal d1 = new BigDecimal("1.0");
BigDecimal d2 = new BigDecimal("0.9");
System.out.println(d1.subtract(d2));

Die Ausgabe ist zweifellos   0.1.

Der Code konnte garantieren, dass die Präzision nicht verloren geht, aber die Teilung in der Mathematik ist möglicherweise unerschöpflich. Wenn wir beispielsweise  10 durch  dividieren 3, wird die folgende Ausnahme ausgelöst:


Um das durch Unteilbarkeit verursachte Problem der unendlichen Dezimalzahlen zu lösen, müssen wir die Genauigkeit der Dezimalzahlen künstlich steuern. Es gibt eine weitere Methode der Divisionsoperation, die zur Steuerung der Präzision verwendet wird:

BigDecimal divide(BigDecimal divisor, int scale, int roundingMode)

scale Der Parameter gibt an, wie viele Dezimalstellen nach der Operation erhalten bleiben, und roundingMode der Parameter gibt an, wie die Dezimalstelle berechnet wird.

BigDecimal d1 = new BigDecimal("1.0");
BigDecimal d2 = new BigDecimal("3");
System.out.println(d1.divide(d2, 2, RoundingMode.DOWN)); // 小数精度为2,多余小数直接舍去。输出结果为0.33

RoundingMode Mit der Aufzählung kann die Dezimalberechnungsmethode bequem angegeben werden. Neben der direkten Rundung gibt es auch mehrere Methoden wie Rundung und Aufrundung, die entsprechend den spezifischen Geschäftsanforderungen angegeben werden können

Beachten Sie, dass die Dezimalgenauigkeit so weit wie möglich im Code und nicht über die Datenbank gesteuert werden sollte. Standardmäßig verwendet die Datenbank Rundungen, um die Dezimalgenauigkeit beizubehalten.

Die in der Datenbank festgelegte Dezimalgenauigkeit beträgt beispielsweise 2,  0.335und wenn ich sie speichere, wird der endgültige gespeicherte Wert zu  0.34.

Nachdem wir nun wissen, wie man  BigDecimal Objekte erstellt und manipuliert, bleibt uns nur noch eine letzte Operation: der Vergleich. Da es sich nicht um einen Basisdatentyp handelt,  == ist es definitiv nicht möglich, doppelte Gleichheitszeichen zu verwenden. Versuchen wir also,  equalseinen Vergleich zu verwenden:

BigDecimal d1 = new BigDecimal("0.33");
BigDecimal d2 = new BigDecimal("0.3300");
System.out.println(d1.equals(d2)); // false

Das Ausgabeergebnis lautet  false: Da  BigDecimal die  equals Methode nicht nur den Wert, sondern auch die Genauigkeit vergleicht, selbst wenn der Wert gleich ist, die Genauigkeit jedoch unterschiedlich ist  false. Wenn Sie feststellen möchten, ob die Werte gleich sind, müssen Sie int compareTo(BigDecimal val)die Methode verwenden:

BigDecimal d1 = new BigDecimal("0.33");
BigDecimal d2 = new BigDecimal("0.3300");
System.out.println(d1.compareTo(d2) == 0); // true

d1 größer als  d2, Rückkehr  1;

d1 kleiner als  d2, zurück  -1;

Wenn die beiden Werte gleich sind, wird zurückgegeben  0.

BigDecimal Die Verwendung von wird hier vorgestellt. Schauen wir uns als nächstes die zweite Lösung an.

Ganzzahl mit fester Länge

Ganzzahlen mit fester Länge sind, wie der Name schon sagt, Ganzzahlen mit fester (dezimaler) Länge. Es ist nur ein Konzept, kein neuer Datentyp, wir verwenden immer noch gewöhnliche Ganzzahlen.

Beträge scheinen wie selbstverständlich Dezimalstellen zu haben, aber wenn man ein wenig darüber nachdenkt, erkennt man, dass Dezimalstellen nicht notwendig sind. Die Einheit des Betrags, den wir zuvor gezeigt haben, ist Yuan , 1.55 also ein Yuan und fünf Cent. Wenn unsere Einheit dann Winkel ist , beträgt der Wert von einem Yuan, fünf Cent und fünf Cent  15.5. Wenn die Einheit dann auf Minuten eingegrenzt wird , beträgt der Wert  155. Richtig, solange die kleinste Einheit erreicht ist, können Dezimalstellen weggelassen werden! Die Mindesteinheit wird entsprechend den Geschäftsanforderungen bestimmt. Wenn das System beispielsweise eine Genauigkeit von Zentimetern erfordert , ist der Wert 1550. Natürlich ist es im Allgemeinen auf den Punkt genau, und die Einheiten, die wir als Nächstes demonstrieren, sind alle Punkte.

Lassen Sie uns jetzt ein neues Feld erstellen, der Typ ist  bigintund die Einheit ist Punkte:

Der entsprechende Datentyp im Code ist natürlich  Long. Wir sind mit den numerischen Operationen grundlegender Typen bestens vertraut und können die Operationsoperatoren direkt verwenden:

long d1 = 10000L; // 100元
d1 += 500L; // 加五元
d1 -= 500L; // 减五元

Über Addition und Subtraktion gibt es nichts zu sagen. Bei Multiplikation und Division kann es Dezimalstellen geben. Wenn beispielsweise ein Produkt um 20 % diskontiert wird, besteht die Operation darin, zu multiplizieren  0.8:

long d1 = 2366L; // 23.66元
double result = d1 * 0.8; // 打八折,运算后结果为1892.8
d1 = (long)result; // 转换为整数,舍去所有小数,值为1892。即18.92元

Bei Dezimaloperationen wird der Typ natürlich zu einer Gleitkommazahl, daher müssen wir die Gleitkommazahl auch in eine Ganzzahl umwandeln.

Bei der erzwungenen Übertragung werden alle Dezimalstellen gerundet. Diese Rundung bedeutet keinen Genauigkeitsverlust . Was ist die Mindesteinheit der Geschäftsanforderungen? Wir behalten nur das, was vorhanden ist, und wir müssen nicht die Einheiten behalten, die niedriger als die Punktzahl sind. Dieser Punkt  BigDecimal stimmt damit überein. Wenn im System nur Punkte benötigt werden, ist die Dezimalgenauigkeit  2, und die restlichen Dezimalstellen werden verworfen.

Einige Geschäftsberechnungen erfordern jedoch möglicherweise andere Operationen wie Rundungen, die wir  Mathüber Klassen durchführen können:

long d1 = 2366L; // 23.66元
double result = d1 * 0.8; // 运算后结果为1892.8
d1 = (long)result; // 强转舍去所有小数,值为1892
d1 = (long)Math.ceil(result); // 向上取整,值为1893
d1 = (long)Math.round(result); // 四舍五入,值为1893
...

Schauen wir uns die Divisionsoperation an. Bei der Division von ganzen Zahlen durch ganze Zahlen werden alle Dezimalstellen automatisch gerundet:

long d1 = 2366L;
long result = d1 / 3; // 正确的值本应该为788.6666666666666,舍去所有小数,最终值为788

Wenn Sie andere Dezimaloperationen wie Rundungen ausführen möchten, führen Sie zuerst Gleitkommaoperationen aus und konvertieren Sie sie dann in Ganzzahlen:

long d1 = 2366L;
double result = d1 / 3.0; // 注意,这里除以不是 3,而是 3.0 浮点数
d1 = (long)Math.round(result); // 四射勿入,最终值为789,即7.89元

Obwohl sowohl die Datenbankspeicherung als auch die Codeoperation Ganzzahlen sind, ist die Anzeige im Front-End immer noch in Cent nicht sehr benutzerfreundlich. Nachdem das Backend den Wert an das Frontend übergeben hat, muss das Frontend den Wert durch sich selbst dividieren  100und ihn dem Benutzer in Yuan anzeigen . Wenn das Front-End den Wert dann an das Back-End übergibt, wird er immer noch als vereinbarte Ganzzahl übergeben.

Ende

Die Abwicklung des Betrages wird erläutert. Wir haben zwei Business-Computing-Szenarien kennengelernt:

  • Dezimaltyp

  • Ganzzahl mit fester Länge

Tatsächlich ist die Informatik im Geschäftsleben technisch gesehen nicht schwierig, aber wenn sie nicht richtig gehandhabt wird, verursacht sie unkalkulierbare Verluste. Schließlich sind Geldangelegenheiten nicht trivial.

Supongo que te gusta

Origin blog.csdn.net/wufaqidong1/article/details/131558942
Recomendado
Clasificación