Wir schreiben unser altes Buchhaltungssystem in VB.NET und SQL Server neu. Wir haben ein neues Team von .NET / SQL-Programmierern hinzugezogen, um das Umschreiben durchzuführen. Der größte Teil des Systems ist bereits mit den Dollarbeträgen unter Verwendung von Floats abgeschlossen. Die von mir programmierte Legacy-Systemsprache hatte kein Float, daher hätte ich wahrscheinlich eine Dezimalzahl verwendet.
Was ist Ihre Empfehlung?
Sollte der Datentyp Float oder Decimal für Dollarbeträge verwendet werden?
Was sind einige der Vor- und Nachteile für beide?
Ein Nachteil, der in unserem täglichen Scrum erwähnt wurde, war, dass Sie vorsichtig sein müssen, wenn Sie einen Betrag berechnen, der ein Ergebnis liefert, das über zwei Dezimalstellen liegt. Es hört sich so an, als müssten Sie den Betrag auf zwei Dezimalstellen runden.
Ein weiterer Nachteil ist, dass alle Anzeigen und gedruckten Beträge eine Formatanweisung haben müssen, die zwei Dezimalstellen anzeigt. Ich bemerkte einige Male, wo dies nicht getan wurde und die Mengen nicht korrekt aussahen. (dh 10,2 oder 10,2546)
Ein Profi ist, dass der Float nur 8 Bytes auf der Festplatte belegt, während die Dezimalstelle 9 Bytes belegt (Dezimalzahl 12,2).
quelle
Antworten:
Die Antwort ist einfach. Schwimmt nie. NIE !
Floats waren gemäß IEEE 754 immer binär, nur die neuen Standard- IEEE 754R definierten Dezimalformate. Viele der gebrochenen binären Teile können niemals der exakten Dezimaldarstellung entsprechen.
Jede Binärzahl kann als
m/2^n
(m
,n
positive ganze Zahlen) geschrieben werden, jede Dezimalzahl alsm/(2^n*5^n)
.Da Binärdateien keine Primzahl haben
factor 5
, können alle Binärzahlen genau durch Dezimalstellen dargestellt werden, aber nicht umgekehrt.Sie erhalten also eine Zahl, die entweder höher oder niedriger als die angegebene Dezimalzahl ist. Immer.
Warum spielt das eine Rolle ? Rundung.
Normale Rundung bedeutet 0..4 nach unten, 5..9 nach oben. Es spielt also eine Rolle, ob das Ergebnis entweder
0.049999999999
... oder0.0500000000
... Sie wissen vielleicht, dass es 5 Cent bedeutet, aber der Computer weiß das nicht und rundet0.4999
... ab (falsch) und0.5000
... auf (richtig) ).Da das Ergebnis von Gleitkommaberechnungen immer kleine Fehlerterme enthält, ist die Entscheidung reines Glück. Es wird hoffnungslos, wenn Sie eine dezimale Rund-zu-Gerade-Behandlung mit Binärzahlen wünschen.
Nicht überzeugt? Sie bestehen darauf, dass in Ihrem Kontosystem alles in Ordnung ist?
Aktiva und Passiva gleich? Ok, dann nimm jede der angegebenen formatierten Zahlen jedes Eintrags, analysiere sie und summiere sie mit einem unabhängigen Dezimalsystem! Vergleichen Sie das mit der formatierten Summe.
Ups, da stimmt etwas nicht, oder?
Hilft nicht gegen diesen Fehler. Da alle Menschen automatisch davon ausgehen, dass der Computer richtig summiert, prüft praktisch niemand unabhängig.
quelle
Dieses Foto antwortet:
Dies ist eine andere Situation: Ein Mann aus Northampton erhielt einen Brief, in dem er sagte, sein Haus würde beschlagnahmt, wenn er nicht null Dollar und null Cent zahlen würde!
quelle
Zuerst sollten Sie dies lesen, was jeder Informatiker über Gleitkomma-Arithmetik wissen sollte . Dann sollten Sie wirklich in Betracht ziehen, eine Art Festpunkt- / Zahlenpaket mit beliebiger Genauigkeit zu verwenden (z. B. Java BigNum, Python-Dezimalmodul), da Sie sonst in eine Welt voller Verletzungen geraten. Stellen Sie dann fest, ob die Verwendung des nativen SQL-Dezimaltyps ausreicht.
Floats / Doubles existieren (ed), um das schnelle x87 fp freizulegen, das jetzt ziemlich veraltet ist. Verwenden Sie sie nicht, wenn Sie sich um die Genauigkeit der Berechnungen kümmern und / oder ihre Einschränkungen nicht vollständig ausgleichen.
quelle
Als zusätzliche Warnung verwenden SQL Server und das .NET-Framework einen anderen Standardalgorithmus zum Runden. Stellen Sie sicher, dass Sie den Parameter MidPointRounding in Math.Round () auschecken. Das .NET-Framework verwendet standardmäßig den Bankers-Algorithmus und SQL Server verwendet die symmetrische algorithmische Rundung. Schauen Sie sich die Wikipedia - Artikel hier
quelle
Fragen Sie Ihre Buchhalter! Sie werden dich wegen der Verwendung von Float missbilligen. Verwenden Sie float wie NUR, wenn Sie nicht auf Genauigkeit achten. Obwohl ich immer dagegen wäre, wenn es um Geld geht.
In der Buchhaltungssoftware ist ein Float NICHT akzeptabel. Verwenden Sie eine Dezimalstelle mit 4 Dezimalstellen.
quelle
Gleitkommazahlen haben unerwartete irrationale Zahlen.
Zum Beispiel können Sie 1/3 nicht als Dezimalzahl speichern, es wäre 0,3333333333 ... (und so weiter)
Floats werden tatsächlich als Binärwert und Potenz von 2 Exponenten gespeichert.
Also wird 1,5 als 3 x 2 zu -1 (oder 3/2) gespeichert
Mit diesen Basis-2-Exponenten werden einige ungerade irrationale Zahlen erzeugt, zum Beispiel:
Konvertieren Sie 1.1 in einen Float und konvertieren Sie ihn dann wieder zurück. Ihr Ergebnis lautet ungefähr: 1.0999999999989
Dies liegt daran, dass die binäre Darstellung von 1.1 tatsächlich 154811237190861 x 2 ^ -47 ist, mehr als ein Double verarbeiten kann.
Mehr zu diesem Thema in meinem Blog , aber im Grunde genommen sind Dezimalstellen für die Speicherung besser geeignet.
Auf Microsoft SQL Server haben Sie den
money
Datentyp - dies ist normalerweise am besten für die finanzielle Speicherung geeignet. Es ist auf 4 Dezimalstellen genau.Für Berechnungen haben Sie eher ein Problem - die Ungenauigkeit ist ein winziger Bruchteil, aber setzen Sie sie in eine Potenzfunktion und es wird schnell signifikant.
Dezimalstellen sind jedoch für keine Art von Mathematik sehr gut - es gibt zum Beispiel keine native Unterstützung für Dezimalstellen.
quelle
Verwenden Sie den Dezimaltyp des SQL Servers .
Verwenden Sie kein Geld und schweben Sie nicht .
Geld verwendet 4 Dezimalstellen, ist schneller als die Verwendung von dezimal BUT leidet unter einigen offensichtlichen und einige nicht so offensichtliche Probleme mit Rundung ( siehe diese connect Ausgabe )
quelle
Was ich empfehlen würde, ist die Verwendung von 64-Bit-Ganzzahlen, die das Ganze in Cent speichern.
quelle
Ein bisschen Hintergrund hier ....
Kein Zahlensystem kann alle reellen Zahlen genau verarbeiten. Alle haben ihre Grenzen, und dies umfasst sowohl das Standard-IEEE-Gleitkomma als auch die vorzeichenbehaftete Dezimalstelle. Der IEEE-Gleitkommawert ist pro verwendetem Bit genauer, aber das spielt hier keine Rolle.
Die Finanzzahlen basieren auf jahrhundertelanger Papier- und Stiftpraxis mit entsprechenden Konventionen. Sie sind ziemlich genau, aber vor allem reproduzierbar. Zwei Buchhalter, die mit verschiedenen Nummern und Tarifen arbeiten, sollten dieselbe Nummer haben. Jeder Raum für Diskrepanzen ist Raum für Betrug.
Daher ist für Finanzberechnungen die richtige Antwort die gleiche Antwort wie ein CPA, der gut rechnet. Dies ist eine Dezimalarithmetik, kein IEEE-Gleitkomma.
quelle
Der einzige Grund, Float für Geld zu verwenden, ist, wenn Sie sich nicht für genaue Antworten interessieren.
quelle
Gleitkommazahlen sind keine exakten Darstellungen, Präzisionsprobleme sind beispielsweise beim Hinzufügen sehr großer und sehr kleiner Werte möglich. Aus diesem Grund werden für die Währung Dezimaltypen empfohlen, auch wenn das Präzisionsproblem möglicherweise selten genug ist.
Zur Verdeutlichung speichert der dezimale 12,2-Typ diese 14 Ziffern genau, während der Float dies nicht tut, da er intern eine binäre Darstellung verwendet. Beispielsweise kann 0,01 nicht genau durch eine Gleitkommazahl dargestellt werden - die nächste Darstellung ist tatsächlich 0,0099999998
quelle
Für ein Bankensystem, an dessen Entwicklung ich beteiligt war, war ich für den Teil "Zinsabgrenzung" des Systems verantwortlich. Mein Code berechnete jeden Tag, wie viel Zinsen auf den Saldo an diesem Tag angefallen waren.
Für diese Berechnung war extreme Genauigkeit und Wiedergabetreue erforderlich (wir haben Oracle FLOAT verwendet), damit wir die aufgelaufenen "Milliardstel eines Pennys" aufzeichnen konnten.
Wenn es darum ging, die Zinsen zu "aktivieren" (dh die Zinsen auf Ihr Konto zurückzuzahlen), wurde der Betrag auf den Cent gerundet. Der Datentyp für die Kontensalden betrug zwei Dezimalstellen. (Tatsächlich war es komplizierter, da es sich um ein Mehrwährungssystem handelte, das mit vielen Dezimalstellen funktionieren konnte - aber wir haben immer auf den "Penny" dieser Währung gerundet). Ja - dort, wo "Bruchteile" von Verlust und Gewinn waren, aber wenn die Computerzahlen aktualisiert wurden (Geld ausgezahlt oder eingezahlt), waren es immer ECHTE Geldwerte.
Dies stellte die Buchhalter, Wirtschaftsprüfer und Tester zufrieden.
Fragen Sie also Ihre Kunden. Sie werden Ihnen ihre Bank- / Buchhaltungsregeln und -praktiken mitteilen.
quelle
Noch besser als die Verwendung von Dezimalstellen ist die Verwendung von einfachen alten Ganzzahlen (oder vielleicht einer Art Bigint). Auf diese Weise haben Sie immer die höchstmögliche Genauigkeit, aber die Genauigkeit kann angegeben werden. Zum Beispiel könnte die Zahl
100
bedeuten1.00
, die wie folgt formatiert ist:int cents = num % 100; int dollars = (num - cents) / 100; printf("%d.%02d", dollars, cents);
Wenn Sie mehr Präzision wünschen, können Sie die 100 in einen größeren Wert ändern, z. B.: 10 ^ n, wobei n die Anzahl der Dezimalstellen ist.
quelle
Eine andere Sache, die Sie in Buchhaltungssystemen beachten sollten, ist, dass niemand direkten Zugriff auf die Tabellen haben sollte. Dies bedeutet, dass der gesamte Zugriff auf das Buchhaltungssystem über gespeicherte Prozesse erfolgen muss. Dies verhindert Betrug und nicht nur SQl-Injektionsangriffe. Ein interner Benutzer, der Betrug begehen möchte, sollte niemals die Möglichkeit haben, Daten in den Datenbanktabellen direkt zu ändern. Dies ist eine kritische interne Kontrolle auf Ihrem System. Möchten Sie wirklich, dass ein verärgerter Mitarbeiter zum Backend Ihrer Datenbank wechselt und diese Schecks schreibt? Oder verbergen Sie, dass sie einem nicht autorisierten Anbieter eine Ausgabe genehmigt haben, wenn sie keine Genehmigungsberechtigung haben? Nur zwei Personen in Ihrer gesamten Organisation sollten in der Lage sein, direkt auf Daten in Ihrer Finanzdatenbank, Ihre Datenbank und seine Sicherung zuzugreifen. Wenn Sie viele dbas haben, sollten nur zwei von ihnen diesen Zugriff haben.
Ich erwähne dies, weil Ihre Programmierer, wenn sie Float in einem Buchhaltungssystem verwenden, wahrscheinlich mit der Idee der internen Kontrollen nicht vertraut sind und sie bei ihren Programmierbemühungen nicht berücksichtigt haben.
quelle
Sie können immer so etwas wie einen Geldtyp für .Net schreiben.
Schauen Sie sich diesen Artikel an: Ein Geldtyp für die CLR - Der Autor hat meiner Meinung nach hervorragende Arbeit geleistet.
quelle
Ich hatte den Geldtyp von SQL zum Speichern von Geldwerten verwendet. Vor kurzem musste ich mit einer Reihe von Online-Zahlungssystemen arbeiten und habe festgestellt, dass einige von ihnen Ganzzahlen zum Speichern von Geldwerten verwenden. In meinen aktuellen und neuen Projekten habe ich angefangen, Ganzzahlen zu verwenden, und ich bin ziemlich zufrieden mit dieser Lösung.
quelle
Von den 100 Brüchen n / 100, wobei n eine natürliche Zahl ist, so dass 0 <= n und n <100, können nur vier als Gleitkommazahlen dargestellt werden. Schauen Sie sich die Ausgabe dieses C-Programms an:
#include <stdio.h> int main() { printf("Mapping 100 numbers between 0 and 1 "); printf("to their hexadecimal exponential form (HEF).\n"); printf("Most of them do not equal their HEFs. That means "); printf("that their representations as floats "); printf("differ from their actual values.\n"); double f = 0.01; int i; for (i = 0; i < 100; i++) { printf("%1.2f -> %a\n",f*i,f*i); } printf("Printing 128 'float-compatible' numbers "); printf("together with their HEFs for comparison.\n"); f = 0x1p-7; // ==0.0071825 for (i = 0; i < 0x80; i++) { printf("%1.7f -> %a\n",f*i,f*i); } return 0; }
quelle
Haben Sie darüber nachgedacht, den Gelddatentyp zum Speichern von Dollarbeträgen zu verwenden?
In Bezug auf die Con, dass die Dezimalstelle ein weiteres Byte einnimmt, würde ich sagen, dass es mir egal ist. In 1 Million Zeilen werden Sie nur noch 1 MB verwenden und der Speicher ist heutzutage sehr billig.
quelle
Was auch immer Sie tun, Sie müssen auf Rundungsfehler achten. Berechnen Sie mit einer höheren Genauigkeit, als Sie in anzeigen.
quelle
Möglicherweise möchten Sie eine Form der Festkomma-Darstellung für Währungswerte verwenden. Sie sollten auch die Rundung von Banker untersuchen (auch als "Round Half Even" bezeichnet). Dadurch werden Verzerrungen vermieden, die bei der üblichen "Round Half Up" -Methode auftreten.
quelle
Verwenden Sie immer Dezimal. Float gibt Ihnen aufgrund von Rundungsproblemen ungenaue Werte.
quelle
Gleitkommazahlen können nur Zahlen darstellen, die eine Summe negativer Vielfacher der Basis sind - für binäre Gleitkommazahlen sind das natürlich zwei.
Es gibt nur vier Dezimalbrüche, die genau im binären Gleitkomma dargestellt werden können: 0, 0,25, 0,5 und 0,75. Alles andere ist eine Annäherung, genauso wie 0,3333 ... eine Annäherung für 1/3 in Dezimalarithmetik ist.
Gleitkomma ist eine gute Wahl für Berechnungen, bei denen die Skalierung des Ergebnisses wichtig ist. Es ist eine schlechte Wahl, wenn Sie versuchen, auf eine bestimmte Anzahl von Dezimalstellen genau zu sein.
quelle
Dies ist ein ausgezeichneter Artikel, der beschreibt, wann float und decimal verwendet werden sollen . Float speichert einen ungefähren Wert und Dezimal speichert einen exakten Wert.
Zusammenfassend sollte für exakte Werte wie Geld eine Dezimalzahl verwendet werden, und für ungefähre Werte wie wissenschaftliche Messungen sollte float verwendet werden.
Hier ist ein interessantes Beispiel, das zeigt, dass sowohl float als auch decimal an Genauigkeit verlieren können. Wenn Sie eine Zahl hinzufügen, die keine Ganzzahl ist, und diese Zahl dann subtrahieren, verliert die Genauigkeit, während die Dezimalzahl dies nicht tut:
DECLARE @Float1 float, @Float2 float, @Float3 float, @Float4 float; SET @Float1 = 54; SET @Float2 = 3.1; SET @Float3 = 0 + @Float1 + @Float2; SELECT @Float3 - @Float1 - @Float2 AS "Should be 0"; Should be 0 ---------------------- 1.13797860024079E-15
Wenn Sie eine Nicht-Ganzzahl multiplizieren und durch dieselbe Zahl dividieren, verlieren Dezimalstellen an Genauigkeit, Floats nicht.
DECLARE @Fixed1 decimal(8,4), @Fixed2 decimal(8,4), @Fixed3 decimal(8,4); SET @Fixed1 = 54; SET @Fixed2 = 0.03; SET @Fixed3 = 1 * @Fixed1 / @Fixed2; SELECT @Fixed3 / @Fixed1 * @Fixed2 AS "Should be 1"; Should be 1 --------------------------------------- 0.99999999999999900
quelle
Ihre Buchhalter möchten steuern, wie Sie runden. Die Verwendung von float bedeutet, dass Sie ständig runden, normalerweise mit einer
FORMAT()
type-Anweisung, die nicht so ist, wie Sie es möchten (verwenden Siefloor
/ceiling
stattdessen).Sie haben Währungsdatentypen (
money
,smallmoney
), die anstelle von float oder real verwendet werden sollten. Durch das Speichern von Dezimalstellen (12,2) werden Ihre Rundungen beseitigt, aber auch in Zwischenschritten - was in einer Finanzanwendung wirklich nicht das ist, was Sie wollen.quelle