Was ist der beste Datentyp für Geld in C #?

Antworten:

420

Wie dezimal beschrieben als:

Das Dezimalschlüsselwort gibt einen 128-Bit-Datentyp an. Im Vergleich zu Gleitkommatypen weist der Dezimaltyp eine höhere Genauigkeit und einen kleineren Bereich auf, wodurch er für finanzielle und monetäre Berechnungen geeignet ist .

Sie können eine Dezimalstelle wie folgt verwenden:

decimal myMoney = 300.5m;
Lee Treveil
quelle
41
Sie sollten erklären, was an diesem Link wichtig ist. Eine Antwort sollte für sich allein gut genug sein, mit einem Link als zusätzlicher Referenz oder Detail. Siehe stackoverflow.com/help/how-to-answer
TheRubberDuck
2
Die Antwort mit der Mindestlänge kann also weniger Zeichen enthalten als der Kommentar mit der Mindestlänge - interessant! Nicht, dass ich ein Problem mit der knappen / prägnanten Antwort habe, besonders wenn sie auch "tief" ist, da sie auf weitere Diskussionen verweist.
B. Clay Shannon
3
Erstaunliche Antwort, und ich glaube nicht, dass es einer weiteren Erklärung bedarf, da sie die Frage vollständig beantwortet. Der Link zur MSDN-Dokumentation ist für mich ein Bonus. Bravo!
Trnelson
@ Leee Treveil, wie das Geld ist (9.0098) bedeutet 4 Zeichen nach Punkt
SAR
114

System.Decimal

Der Dezimalwerttyp repräsentiert Dezimalzahlen im Bereich von positiv 79.228.162.514.264.337.593.543.950.335 bis negativ 79.228.162.514.264.337.593.543.950.335. Der Dezimalwerttyp eignet sich für Finanzberechnungen, bei denen eine große Anzahl signifikanter Integral- und Bruchziffern und keine Rundungsfehler erforderlich sind. Der Dezimaltyp beseitigt nicht die Notwendigkeit einer Rundung. Vielmehr werden Fehler aufgrund von Rundungen minimiert.

Ich möchte auf diese hervorragende Antwort von zneak hinweisen, warum double nicht verwendet werden sollte.

David Walschots
quelle
68

Verwenden Sie das Money-Muster aus Patterns of Enterprise Application Architecture . Geben Sie den Betrag als Dezimalzahl und die Währung als Aufzählung an.

lmsasu
quelle
2
Eigentlich wollte ich das vorschlagen, aber ich mache Währung zu einer Klasse, damit ich einen Wechselkurs definieren kann (in Bezug auf eine "Basiswährung", oft den US-Dollar [den ich auf einen Wechselkurs von 1,00 festgelegt habe]).
Thomas Owens
5
Für die zukünftigen Besucher dieses Threads (wie ich) gibt es jetzt Folgendes : nuget.org/packages/Money und es rockt!
Korijn
Ich frage mich, ob ein solcher Typ eine Struktur oder Klasse sein sollte. Eine Dezimalzahl + eine (int) Aufzählung ergibt 20 Bytes. Mein Geld ist immer noch auf Struktur.
Nawfal
Das MoneyNuget hat einen toten Github-Link für die Projektsite, also ... keine Dokumente?
George Mauer
Das Problem dabei ist, dass Sie beim Erstellen Ihrer eigenen Implementierung herausfinden müssen, wie Sie diese tatsächlich beibehalten können. Und das beliebteste ORM (EF) unterstützt überhaupt keine benutzerdefinierten Datentypen. Deshalb wird jemand gebeten, wirklich tief in das Unkraut einzudringen, um das zu tun, was ziemlich einfach sein sollte.
George Mauer
25

Dezimal. Wenn Sie Double wählen, sind Sie offen für Rundungsfehler

SquidScareMe
quelle
8
@Jess doublekann Rundungsfehler verursachen, da Gleitkomma nicht alle Zahlen exakt darstellen kann (z. B. 0,01 hat keine exakte Darstellung im Gleitkomma). Decimal, Auf der anderen Seite, ist für Zahlen genau . (Der Kompromiss Decimalhat einen kleineren Bereich als Gleitkomma) Gleitkomma kann zu * unbeabsichtigten * Rundungsfehlern führen (z 0.01+0.01 != 0.02. B. ). Decimalkann Ihnen Rundungsfehler geben, aber nur, wenn Sie danach gefragt haben (z. B. Math.Round(0.01+0.02)gibt Null zurück)
Ian Boyd
2
@ IanBoyd: Der Wert "$ 1.57" kann genau dargestellt werden (doppelt) 157. Wenn doubleSkalierung und gegebenenfalls domänenspezifische Rundung verwendet und sorgfältig angewendet werden, kann dies vollkommen präzise sein. Wenn man in seiner Rundung schlampig ist, decimalkann dies zu semantisch falschen Ergebnissen führen (z. B. wenn man mehrere Werte addiert, die auf den nächsten Cent gerundet werden sollen, aber nicht zuerst um sie herum). Das einzig gute daran decimalist, dass die Skalierung integriert ist.
Supercat
1
@supercat, zu diesem Kommentar "Wenn man mehrere Werte addiert, die auf den nächsten Cent gerundet werden sollen, aber nicht zuerst um sie herum", sehe ich nicht, wie ein Float dies lösen würde. Es ist ein Benutzerfehler und hat meiner Meinung nach nichts mit Dezimalstellen zu tun. Ich verstehe den Punkt, aber ich glaube, es wurde verlegt, hauptsächlich weil IanBoyd das spezifiziert hat ... wenn Sie danach fragen.
Sawe
13

Stimmen Sie dem Geldmuster zu: Der Umgang mit Währungen ist einfach zu umständlich, wenn Sie Dezimalstellen verwenden.

Wenn Sie eine Währungsklasse erstellen, können Sie dort die gesamte Geldlogik ablegen, einschließlich einer korrekten ToString () - Methode, einer besseren Kontrolle der Parsing-Werte und einer besseren Kontrolle der Divisionen.

Außerdem besteht bei einer Währungsklasse keine Möglichkeit, dass unbeabsichtigt Geld mit anderen Daten verwechselt wird.

Lennaert
quelle
10

Eine andere Option (insbesondere wenn Sie Ihre eigene Klasse rollen) besteht darin, ein int oder ein int64 zu verwenden und die unteren vier Ziffern (oder möglicherweise sogar 2) als "rechts vom Dezimalpunkt" zu kennzeichnen. "An den Rändern" benötigen Sie also "* 10000" auf dem Weg nach innen und etwas "/ 10000" auf dem Weg nach draußen. Dies ist der von SQL Server von Microsoft verwendete Speichermechanismus (siehe http://msdn.microsoft.com/en-au/library/ms179882.aspx)

Das Schöne daran ist, dass Ihre gesamte Summierung mit (schneller) Ganzzahlarithmetik durchgeführt werden kann.

dsz
quelle
7

Die meisten Anwendungen, mit denen ich gearbeitet habe, verwenden decimal, um Geld darzustellen. Dies basiert auf der Annahme, dass sich die Anwendung niemals mit mehr als einer Währung befasst.

Diese Annahme kann auf einer anderen Annahme beruhen, dass die Anwendung niemals in anderen Ländern mit anderen Währungen verwendet wird. Ich habe Fälle gesehen, in denen sich das als falsch erwiesen hat.

Jetzt wird diese Annahme auf eine neue Art und Weise in Frage gestellt: Neue Währungen wie Bitcoin werden immer häufiger und sind für kein Land spezifisch. Es ist nicht unrealistisch, dass eine Anwendung, die nur in einem Land verwendet wird, möglicherweise noch mehrere Währungen unterstützen muss.

Einige Leute werden sagen, dass das Erstellen oder sogar Verwenden eines Typs nur für Geld "Vergoldung" ist oder zusätzliche Komplexität über die bekannten Anforderungen hinaus hinzufügt. Ich bin absolut anderer Meinung. Je allgegenwärtiger ein Konzept in Ihrer Domäne ist, desto wichtiger ist es, angemessene Anstrengungen zu unternehmen, um die richtige Abstraktion im Voraus zu verwenden. Wenn Sie die Komplexität sehen möchten, versuchen Sie, in einer Anwendung zu arbeiten, die früher verwendet wurde. decimalJetzt gibt es Currencyneben jeder decimalEigenschaft eine zusätzliche Eigenschaft.

Wenn Sie die falsche Abstraktion im Voraus verwenden, ist das spätere Ersetzen hundertmal mehr Arbeit. Dies bedeutet, dass möglicherweise Fehler in vorhandenen Code eingefügt werden, und das Beste daran ist, dass diese Fehler wahrscheinlich Geldbeträge, Transaktionen mit Geld oder einfach alles mit Geld betreffen.

Und es ist nicht so schwierig, etwas anderes als Dezimalzahl zu verwenden. Google "Nuget Money Type" und Sie werden sehen, dass zahlreiche Entwickler solche Abstraktionen erstellt haben (einschließlich mir). Es ist einfach. Es ist so einfach wie das Verwenden DateTimeeines Datums in einem string.

Scott Hannen
quelle
5

Erstelle deine eigene Klasse. Dies scheint seltsam, aber ein .Net-Typ reicht nicht aus, um verschiedene Währungen abzudecken.

Noel Kennedy
quelle