Darstellung von Geldwerten in Java [geschlossen]

94

Ich verstehe, dass BigDecimal als Best Practice für die Darstellung von Geldwerten in Java empfohlen wird. Was benutzt du? Gibt es eine bessere Bibliothek, die Sie lieber verwenden?

dshaw
quelle
4
Werfen
1
Hier ist eine Währungsklasse, die Sie kopieren und erweitern können: java-articles.info/articles/?p=254
Gilbert Le Blanc
Siehe auch die Referenzimplementierung von JSR-354 github.com/JavaMoney/jsr354-ri
Beat

Antworten:

81

BigDecimalden ganzen Weg. Ich habe von einigen Leuten gehört, die ihre eigenen Cashoder MoneyKlassen erstellen , die einen Barwert mit der Währung kapseln, aber unter der Haut ist es immer noch eine BigDecimal, wahrscheinlich mit BigDecimal.ROUND_HALF_EVENRundung.

Bearbeiten: Wie Don in seiner Antwort erwähnt , gibt es Open-Source-Projekte wie timeandmoney , und obwohl ich sie dafür begrüße, dass sie versuchen, Entwickler daran zu hindern, das Rad neu zu erfinden, habe ich einfach nicht genug Vertrauen in eine Pre-Alpha-Bibliothek, um sie zu verwenden es in einer Produktionsumgebung. Übrigens, wenn Sie unter der Haube graben um, sehen Sie sie verwenden BigDecimalzu .

neunseitig
quelle
4
+1. Wir haben beschlossen, eine Containerklasse hinzuzufügen, die auch eine Währung verbraucht. Dies ist praktisch, wenn Sie Geldwerte in Tabellen rendern.
Daniel Hiller
1
Ja, das ist ein ziemlich verbreiteter Ansatz und macht sehr viel Sinn. Eine Einschränkung ist, wenn Sie sich mit japanischen Yen befassen müssen, da diese keine geringe Währungsbezeichnung wie Cent haben und daher eigene Rundungsregeln benötigen.
Neunseitiger
3
@ninesided gibt ein großartiges Beispiel dafür, warum das Rollen Ihrer eigenen eine schlechte Antwort ist. "Oh, und übrigens funktioniert es nicht für $ CURRENCY_X." Das ist ein gutes Zeichen dafür, dass es auch für viele andere Währungen nicht funktioniert.
James Moore
1
@JamesMoore Ich bin nicht der Meinung, dass "Rolling Your Own" ein schlechter Ansatz ist. Sie müssen sich nur der möglichen Einschränkungen des von Ihnen gewählten Ansatzes bewusst sein, daher der Grund, warum ich ihn erwähne. Es ist trivial, unterschiedliche Rundungsregeln pro Währung zu implementieren. Wenn Ihr System jedoch nur mit USD oder EUR handeln muss, müssen Sie die Dinge nicht überentwickeln.
Neunseitiger
1
Schauen Sie sich stackoverflow.com/questions/5134237/… nur aus einem Grund an, warum BigDecimal ein Problem ist. Die weltweite Buchhaltung ist nur ein Sumpf von Sonderfällen, und der Versuch, sie alle unter den Teppich von BigDecimal zu kehren, funktioniert einfach nicht.
James Moore
52

Es kann für Leute, die von Suchmaschinen hierher kommen, nützlich sein, etwas über JodaMoney zu erfahren: http://www.joda.org/joda-money/ .

Iñaki Ibarrola Atxa
quelle
Vielen Dank. Ich wollte einen Follow-up-Hinweis zu Joda Money hinzufügen. Hast du es benutzt?
Dshaw
2
+1 sieht interessant aus, froh zu sehen, dass es BigDecimalunter der Haube ist!
Neunseitiger
8

Eine praktische Bibliothek, auf die ich früher gestoßen bin, ist die Joda-Money- Bibliothek. Eine seiner Implementierungen basiert in der Tat auf BigDecimal. Es basiert auf der ISO-4217- Spezifikation für Währungen und kann eine benutzerdefinierte Währungsliste (geladen über CVS) unterstützen.

Diese Bibliothek verfügt über eine kleine Anzahl von Dateien, die bei Bedarf schnell durchsucht werden können. Joda-Money wird unter der Apache 2.0-Lizenz veröffentlicht.

Bashar
quelle
7

Wenn Sie nur Dollar und Cent verwenden, würde ich eine lange (um 2 Dezimalstellen versetzt) ​​verwenden. Wenn Sie mehr Details benötigen, ist möglicherweise eine große Dezimalstelle der richtige Weg.

In beiden Fällen würde ich die Klasse wahrscheinlich erweitern, um eine .toString () zu haben, die das richtige Format verwendet, und um andere Methoden zu platzieren, die möglicherweise auftauchen (Für eine lange Zeit wird das Multiplizieren und Teilen schief gehen, wenn die Dezimalstelle nicht stimmt nicht angepasst)

Wenn Sie Ihre eigene Klasse und Schnittstelle definieren, können Sie die Implementierung nach Belieben ersetzen.

Bill K.
quelle
2
Achtung, selbst lange könnten zu kurz sein, um die US-Bundesverschuldung in Cent zu halten ... wenn nicht jetzt, dann in ein paar Jahren.
Ingo
3
Ich stimme zu - jede große Anzahl von Dollar (oder wenn Sie Geld in Yen verfolgen) sollten Sie BigDecimal verwenden - aber selbst dann würde ich ernsthaft in Betracht ziehen, eine Containerklasse dafür zu verwenden. Ich denke, die meiste Programmierkomplexität kommt von Leuten, die keine kleinen, einfachen Klassen für Sammlungen und intrinsische Typen definieren.
Bill K
3

BigDecimal oder eine andere Festpunktdarstellung ist das, was im Allgemeinen für Geld benötigt wird.

Gleitkomma ( Double, Float) -Darstellungen und -Berechnungen sind ungenau und führen zu fehlerhaften Ergebnissen.

Ken Gentle
quelle
7
Genau genommen ist BigDecimal auch ungenau. Es entspricht einfach besser der Dezimalrundung, die wir im täglichen Leben gewohnt sind, und ermöglicht es Ihnen, Rundungsmodi festzulegen.
Michael Borgwardt
1
@Michael Borgwardt BigDecimal unterscheidet sich von IEEE FP darin, dass eine explizite Skala angegeben wird. Obwohl nicht alle Operationen genau sind, stellt dies sicher, dass eine Reihe von Operationen und Verhaltensweisen immer genau sind und die Skalierung konstant ist, während die Skalierung für IEEE FP mit dem Wert abnimmt.
1
Was hat das mit Geld zu tun? Buchhaltungsorganisationen auf der ganzen Welt haben normalerweise sehr spezielle Anforderungen an die Art und Weise, wie Sie in ihrer Währung rechnen. Entspricht BigDecimal genau jedem dieser Standards? Wird es dies nächstes Jahr tun, wenn sich diese Standards ändern? Und BigDecimal gibt nicht einmal annähernd nützliche Rundungsregeln für Währungen an.
James Moore
2

Man muss so vorsichtig sein, wenn man mit Zeit und Geld umgeht.

Wenn Sie mit Geld arbeiten, hoffe ich, dass jeder wissen sollte, niemals einen Float oder ein Double zu verwenden.

Bei BigDecimal bin ich mir jedoch nicht sicher.

In den meisten Fällen geht es Ihnen gut, wenn Sie nur die Cent in einem Int oder Long verfolgen. Auf diese Weise können Sie niemals mit einer Dezimalstelle umgehen.

Sie zeigen Dollars nur an, wenn Sie sie drucken. Arbeiten Sie immer mit internen Cent mit ganzen Zahlen. Dies kann schwierig sein, wenn Sie sich teilen oder Math.abs () verwenden müssen.

Möglicherweise ist Ihnen jedoch ein halber Cent oder sogar ein Hundertstel Cent wichtig. Ich weiß nicht, was ein guter Weg ist, dies zu tun. Möglicherweise müssen Sie nur mit Tausendstel Cent umgehen und eine lange verwenden. Oder Sie werden gezwungen sein, BigDecimal zu verwenden

Ich würde viel mehr darüber lesen, aber jeden ignorieren, der anfängt, über die Verwendung eines Floats oder Double zur Darstellung von Geld zu sprechen. Sie bitten nur um Ärger.

Ich bin der Meinung, dass mein Rat nicht vollständig ist. Bitte geben Sie mehr darüber an. Sie haben es mit gefährlichen Typen zu tun!

Pyrolistisch
quelle
2
Warum müssten Sie "gezwungen" werden, BigDecimal zu verwenden? Worüber bist du dir nicht sicher? Es ist der Arbeit mit Cent deutlich überlegen, da Sie damit Rundungsmodi explizit festlegen können.
Michael Borgwardt
1
@MichaelBorgwardt: Ja, Sie können eine winzige Teilmenge der Rundungsmodi angeben, die Sie für Währungen benötigen. So? (Hinweis: Rundungswährungen werden normalerweise von nationalen Buchhaltungsorganisationen festgelegt. Sie werfen gerne seltsame Sonderfälle ein. Unter stackoverflow.com/questions/5134237/… finden Sie nur einen der vielen unterhaltsamen Gründe, warum die Rundung von BigDecimal vollständig ist hier nutzlos.)
James Moore
@ James: Wie genau ist es "nutzlos"? Wie wäre die Implementierung dieser Sonderfälle mit BigDecimal schwieriger als mit etwas anderem?
Michael Borgwardt
1
OK, völlig nutzlos ist zu stark. In der komplizierten Klasse, die die Währung abstrahiert, sind die Rundungsregeln von BigDecimal wahrscheinlich in bestimmten Fällen nützlich, um eine Teilmenge der Art und Weise zu erstellen, wie die Währungsrundung erfolgt. Der allgemeine Fall ist jedoch, dass Rundungsregeln für Währungen Mechanismen erfordern, die sich im Laufe der Zeit ändern können (da menschliche Rechnungslegungsagenturen die Regeln bilden und diese frei ändern können). Bei der Frage geht es nicht um Euro (oder was auch immer den Euro im nächsten Monat ersetzt ...) oder um Dollar im Jahr 2011, sondern um Währung. Sie müssen sich also mit einer Menge unangenehmer Komplexität auseinandersetzen.
James Moore
2

Das Erstellen einer Geldklasse ist der richtige Weg. Verwenden Sie BigDecimal (oder sogar ein Int) darunter. Verwenden Sie dann die Währungsklasse, um die Rundungskonvention zu definieren.

Leider macht es Java ohne Überlastung des Operators ziemlich unangenehm, solche Grundtypen zu erstellen.

Kozyarchuk
quelle
2

Es gibt eine bessere Bibliothek, Zeit und Geld . IMO ist es den vom JDK zur Darstellung dieser beiden Konzepte bereitgestellten Bibliotheken weit überlegen.

Dónal
quelle
3
Diese Antwort wurde vor drei Jahren veröffentlicht. Noch heute ist das TimeandMoney-Projekt laut diesem Link Pre-Alpha.
James Moore
1
@ JamesMoore Guter Anruf. Die Antwort ist jetzt 7 Jahre alt und das Projekt ist immer noch nicht stabil.
Navin
1

Auf keinen Fall BigDecimal. Es gibt so viele spezielle Regeln für Rundungen und Präsentationen, über die Sie sich Sorgen machen müssen.

Martin Fowler empfiehlt die Implementierung einer dedizierten Geldklasse zur Darstellung von Währungsbeträgen, die auch Regeln für die Währungsumrechnung implementiert.

Lindelof
quelle
6
und der zugrunde liegende Datentyp seiner Money-Klasse? BigDecimal.
Neunseitiger
1
Das ist nicht wahr. Sie könnten Integer in der Geldklasse verwenden, was Martin auch tut. Ich habe das schon oft gemacht.
Egervari
Die Empfehlung ist jedoch richtig; Berechnungen mit Geld sind ein riesiger Sumpf von Sonderfällen, die sich im Laufe der Zeit ändern. BigDecimal mag als winziger Teil der Lösung nützlich sein, ist aber sicherlich nicht allgemein.
James Moore
1

Hey, hier ist ein sehr interessanter Artikel über BigDecimal und ein anschauliches Beispiel dafür, warum es manchmal anstelle von Doppel verwendet wird. BigDecimal Tutorial .

arg20
quelle
0

Sie können die DecimalFormat-Klasse verwenden, wenn Sie letztendlich einen Währungswert anzeigen. Es bietet Lokalisierungsunterstützung und ist ziemlich erweiterbar.


quelle
0

Ich würde BigDecimal in die Money-Klasse einkapseln, die auch eine Währung hat, genau wie jemand, der oben erwähnt wurde. Das Wichtigste ist, dass Sie extrem viele Unit-Tests durchführen, insbesondere wenn Sie mit verschiedenen Währungen arbeiten. Es ist auch eine gute Idee, wenn Sie einen praktischen Konstruktor hinzufügen, der eine Zeichenfolge verwendet, oder eine Factory-Methode, die dasselbe tut, damit Sie Ihre Tests wie folgt schreiben können:

   assertEquals(Money.create("100.0 USD").add("10 GBP"),Money.create("116 USD"));
Per Arneng
quelle
0

Es gibt immer Einschränkungen und Besonderheiten. Jeder, der nicht über ausreichende Erfahrung verfügt, um die im folgenden Artikel beschriebenen subtilen Probleme zu verstehen, sollte dies vor dem Umgang mit realen Finanzdaten ernsthaft überdenken:

http://lemnik.wordpress.com/2011/03/25/bigdecimal-and-your-money

BigDecimal ist kaum die einzig richtige Darstellung oder das einzige Puzzleteil. Unter bestimmten Bedingungen könnte die Verwendung einer Money-Klasse, die durch als Ganzzahl gespeicherte Cent unterstützt wird, ausreichend sein und wäre viel schneller als BigDecimal. Ja, das impliziert die Verwendung von Dollar als Währung und Grenzbeträge, aber solche Einschränkungen sind für viele Anwendungsfälle durchaus akzeptabel, und alle Währungen haben ohnehin Sonderfälle für Rundungen und Teilbezeichnungen, sodass es keine "universelle" Lösung gibt.

Craig
quelle
1
Dies scheint ein Kommentar zu einem anderen Beitrag zu sein, anstatt eine tatsächliche Antwort. Es ist auch übermäßig lebensgefährlich. Bitte versuchen Sie in Zukunft höflicher zu sein.
Slater Victoroff