Représentation des valeurs monétaires en Java [fermé]

94

Je comprends que BigDecimal est la meilleure pratique recommandée pour représenter les valeurs monétaires en Java. Qu'est ce que tu utilises? Y a-t-il une meilleure bibliothèque que vous préférez utiliser à la place?

dshaw
la source
4
jetez un oeil à JSR 354
yegor256
1
Voici une classe de devises que vous pouvez copier et étendre: java-articles.info/articles/?p=254
Gilbert Le Blanc
Voir également l'implémentation de référence de JSR-354 github.com/JavaMoney/jsr354-ri
battre

Réponses:

81

BigDecimaltout le. J'ai entendu parler de certaines personnes créant leur propre Cashou des Moneyclasses qui encapsulent une valeur monétaire avec la monnaie, mais sous la peau, c'est toujours un BigDecimal, probablement avec des BigDecimal.ROUND_HALF_EVENarrondis.

Edit: Comme Don le mentionne dans sa réponse , il existe des projets open source comme timeandmoney , et bien que je les félicite d'avoir essayé d'empêcher les développeurs d'avoir à réinventer la roue, je n'ai tout simplement pas assez de confiance dans une bibliothèque pré-alpha à utiliser. dans un environnement de production. De plus, si vous creusez sous le capot, vous verrez qu'ils l'utilisent BigDecimalaussi .

neuf côtés
la source
4
+1. Nous avons décidé d'ajouter une classe de conteneur qui consomme également une devise. Cela est pratique lors du rendu des valeurs monétaires dans les tableaux.
Daniel Hiller
1
oui, c'est une approche assez courante et cela a beaucoup de sens. Une mise en garde à cela est lorsque vous devez traiter avec le yen japonais, car ils n'ont pas de dénomination de monnaie mineure comme les cents, il a donc besoin de ses propres règles d'arrondi.
neuf du
3
@ninesided donne un excellent exemple de la raison pour laquelle rouler le vôtre est une mauvaise réponse. "Oh, et au fait, ça ne marche pas pour $ CURRENCY_X." C'est un bon signe que cela ne fonctionne pas non plus pour beaucoup d'autres devises.
James Moore
1
@JamesMoore Je ne suis pas d'accord pour dire que "rouler soi-même" est une mauvaise approche, il vous suffit d'être conscient des limites possibles de l'approche que vous choisissez, d'où la raison pour laquelle je la mentionne. Il est trivial de mettre en œuvre différentes règles d'arrondi par devise, mais si votre système n'a besoin de traiter qu'en USD ou en EUR, vous n'avez pas besoin de trop d'ingénierie.
1er
1
Jetez un œil à stackoverflow.com/questions/5134237/… pour une seule raison pour laquelle BigDecimal est un problème. La comptabilité à l'échelle de la planète n'est qu'un marais de cas spéciaux, et essayer de tous les balayer sous le tapis de BigDecimal ne fonctionne tout simplement pas.
James Moore
52

Il peut être utile aux personnes arrivant ici par les moteurs de recherche de connaître JodaMoney: http://www.joda.org/joda-money/ .

Iñaki Ibarrola Atxa
la source
Merci. Je voulais ajouter une note de suivi sur Joda Money. L'avez-vous utilisé?
dshaw le
2
+1 semble intéressant, heureux de voir que c'est BigDecimalsous le capot!
1er
8

Une bibliothèque pratique que j'ai rencontrée plus tôt est la bibliothèque Joda-Money . Une de ses implémentations est en effet basée sur BigDecimal. Il est basé sur la spécification ISO-4217 pour les devises et peut prendre en charge une liste de devises personnalisée (chargée via CVS).

Cette bibliothèque contient un petit nombre de fichiers que l'on peut parcourir rapidement si des modifications sont nécessaires. Joda-Money est publié sous la licence Apache 2.0.

Bashar
la source
7

Si vous n'utilisez que des dollars et des cents, j'utiliserais un long (décalé de 2 décimales). Si vous avez besoin de plus de détails, la grande décimale peut être la solution.

Quoi qu'il en soit, j'étendrais probablement la classe pour avoir un .toString () qui utilise le format correct, et comme un endroit pour mettre d'autres méthodes qui pourraient apparaître (pendant longtemps, multiplier et diviser ira de travers si la décimale n'est pas pas ajusté)

De plus, si vous définissez votre propre classe et interface, vous pouvez remplacer l'implémentation à volonté.

Bill K
la source
2
Attention, même long pourrait être trop court pour détenir la dette fédérale américaine en cents ... sinon maintenant, dans quelques années.
Ingo
3
Je suis d'accord - pour tout nombre énorme de dollars (ou peut-être si vous suivez de l'argent en yens), vous devriez utiliser BigDecimal - mais même dans ce cas, j'envisagerais sérieusement d'utiliser une classe de conteneur pour cela. Je pense que la plupart de la complexité de la programmation vient de personnes qui ne définissent pas de petites classes simples autour des collections et des types intrinsèques.
Bill K
3

BigDecimal ou une autre représentation en virgule fixe est ce qui est généralement nécessaire pour l'argent.

Les représentations et calculs en virgule flottante ( Double, Float) sont inexacts, ce qui conduit à des résultats erronés.

Ken Gentle
la source
7
Strictement parlant, BigDecimal est également inexact; il correspond mieux à l'arrondi décimal auquel nous sommes habitués dans la vie quotidienne et vous permet de spécifier des modes d'arrondi.
Michael Borgwardt
1
@Michael Borgwardt BigDecimal diffère d'IEEE FP en ce qu'une échelle explicite est spécifiée. Bien que toutes les opérations ne soient pas exactes, cela garantit qu'un ensemble d'opérations et de comportements est toujours exact et que l'échelle est constante, tandis que l'échelle pour IEEE FP diminue avec la valeur.
1
Qu'est-ce que cela a à voir avec l'argent? Les organisations comptables du monde entier ont généralement des exigences très spécifiques sur la façon dont vous faites des mathématiques dans leur devise. BigDecimal correspond-il précisément à chacune de ces normes? Le fera-t-il l'année prochaine, lorsque ces normes changeront? Et BigDecimal n'est même pas près de spécifier des règles d'arrondi utiles pour les devises.
James Moore
2

Vous devez être très prudent en ce qui concerne le temps et l'argent.

Lorsque vous travaillez avec de l'argent, j'espère que tout le monde devrait savoir qu'il ne faut jamais utiliser de flotteur ou de double.

Mais je ne suis pas sûr de BigDecimal.

Dans la plupart des cas, tout ira bien si vous gardez simplement une trace des centimes dans un int ou un long. De cette façon, vous ne traitez jamais avec une décimale.

Vous n'affichez que les dollars lorsque vous l'imprimez. Travaillez toujours avec des centimes internes en utilisant des entiers. Cela peut être délicat si vous devez diviser ou utiliser Math.abs ().

Cependant, vous pourriez vous soucier d'un demi-cent, voire d'un centième de cent. Je ne sais pas quelle est la bonne façon de faire cela. Vous devrez peut-être simplement traiter un millième de centimes et utiliser un long. Ou peut-être serez-vous obligé d'utiliser BigDecimal

Je ferais beaucoup plus de lecture à ce sujet, mais ignorez tous ceux qui commencent à parler d'utiliser un flotteur ou un double pour représenter l'argent. Ils demandent juste des ennuis.

Je pense que mon conseil n'est pas complet, alors s'il vous plaît, mettez-y davantage. Vous avez affaire à des types dangereux!

Pyrolistique
la source
2
Pourquoi auriez-vous besoin d'être "forcé" à utiliser BigDecimal? De quoi n'êtes-vous pas sûr? C'est clairement supérieur à travailler avec des cents, car cela vous permet de spécifier explicitement les modes d'arrondi.
Michael Borgwardt
1
@MichaelBorgwardt: oui, cela vous permet de spécifier un petit sous-ensemble des modes d'arrondi dont vous avez besoin pour les devises. Alors? (Astuce: l'arrondissement des devises est généralement décidé par les organisations comptables nationales. Ils sont parfaitement heureux de lancer dans des cas spéciaux étranges. Voir stackoverflow.com/questions/5134237/… pour l'une des nombreuses raisons amusantes pour lesquelles l'arrondi BigDecimal est complètement inutile ici.)
James Moore
@James: En quoi est-ce exactement "inutile"? Comment la mise en œuvre de ces cas particuliers serait-elle plus difficile avec BigDecimal qu'avec autre chose?
Michael Borgwardt
1
OK, complètement inutile est trop fort. Dans la classe compliquée qui fait abstraction de la devise, les règles d'arrondi de BigDecimal sont probablement utiles dans certains cas spécifiques pour créer un sous-ensemble des modes d'arrondi de devise. Mais le cas général est que les règles d'arrondi pour les devises nécessitent des mécanismes susceptibles de changer au fil du temps (puisque les agences comptables humaines composent les règles et sont libres de les changer). La question n'est pas sur l'euro (ou ce qui remplace l'euro le mois prochain ...), ou sur le dollar en 2011, c'est sur la monnaie, donc vous devez faire face à beaucoup de complexité désagréable.
James Moore
2

Créer une classe Money est la voie à suivre. En utilisant BigDecimal (ou même un int) en dessous. Ensuite, utilisez la classe Currency pour définir la convention d'arrondi.

Malheureusement, sans surcharger les opérateurs, Java rend assez désagréable la création de tels types de base.

Kozyarchuk
la source
2

Il y a une meilleure bibliothèque, du temps et de l' argent . IMO, il est de loin supérieur aux bibliothèques fournies par le JDK pour représenter ces 2 concepts.

Dónal
la source
3
Cette réponse a été publiée il y a trois ans. Aujourd'hui, le projet timeandmoney est toujours pré-alpha selon ce lien.
James Moore
1
@JamesMoore Bon appel. La réponse a maintenant 7 ans et le projet n'est toujours pas stable.
Navin le
1

Certainement pas BigDecimal. Il y a tellement de règles spéciales d'arrondi et de présentation que vous devez vous inquiéter.

Martin Fowler recommande la mise en œuvre d'une classe Money dédiée pour représenter les montants en devises, et qui implémente également des règles de conversion de devises.

Lindelof
la source
6
et le type de données sous-jacent de sa classe Money? BigDecimal.
9
1
Ce n'est pas vrai. Vous pouvez utiliser Integer dans la classe money, ce que fait Martin. J'ai fait cela plusieurs fois.
egervari
La recommandation est correcte cependant; les calculs impliquant de l'argent sont un vaste marais de cas particuliers qui changent avec le temps. BigDecimal peut être utile en tant que petite partie de la solution, mais ce n'est certainement pas général.
James Moore
1

Hé, voici un article très intéressant sur BigDecimal, et un exemple illustratif de la raison pour laquelle il est parfois utilisé au lieu de doubles. Tutoriel BigDecimal .

arg20
la source
0

Vous pouvez utiliser la classe DecimalFormat lors de l'affichage final d'une valeur monétaire. Il fournit un support de localisation et est assez extensible.


la source
0

J'encapsulerais BigDecimal dans la classe Money qui a également une devise, tout comme quelqu'un mentionné ci-dessus. L'important est que vous fassiez une quantité extrême de tests unitaires et surtout si vous travaillez avec des devises différentes. C'est aussi une bonne idée si vous ajoutez un constructeur pratique qui prend une chaîne ou une méthode d'usine qui fait la même chose afin que vous puissiez écrire vos tests quelque chose comme ceci:

   assertEquals(Money.create("100.0 USD").add("10 GBP"),Money.create("116 USD"));
Par Arneng
la source
0

Il y a toujours des contraintes et des spécificités impliquées. Toute personne sans expérience suffisante pour apprécier les problèmes subtils décrits dans l'article suivant devrait sérieusement reconsidérer avant de traiter des données financières réelles:

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

BigDecimal est loin d'être la seule représentation correcte ou la seule pièce du puzzle. Dans certaines conditions, l'utilisation d'une classe Money adossée à des cents stockés sous forme d'entier pourrait être suffisante et serait beaucoup plus rapide que BigDecimal. Oui, cela implique l'utilisation de dollars comme monnaie et limite les montants mais de telles contraintes sont parfaitement acceptables pour de nombreux cas d'utilisation et toutes les devises ont de toute façon des cas particuliers d'arrondis et de sous-dénominations, il n'y a donc pas de solution "universelle".

Craig
la source
1
Cela semble être un commentaire sur un autre message au lieu d'une réponse réelle. C'est aussi trop vitriolique. Veuillez essayer d'être plus courtois à l'avenir.
Slater Victoroff