Representing Monetary Values in Java [closed]
Asked Answered
C

14

95

I understand that BigDecimal is recommended best practice for representing monetary values in Java. What do you use? Is there a better library that you prefer to use instead?

Caveman answered 12/11, 2008 at 22:49 Comment(3)
take a look at JSR 354Randirandie
Here's one Currency class that you can copy and extend: java-articles.info/articles/?p=254Palsy
Also see the reference implementation of JSR-354 github.com/JavaMoney/jsr354-riTineid
L
83

BigDecimal all the way. I've heard of some folks creating their own Cash or Money classes which encapsulate a cash value with the currency, but under the skin it's still a BigDecimal, probably with BigDecimal.ROUND_HALF_EVEN rounding.

Edit: As Don mentions in his answer, there are open sourced projects like timeandmoney, and whilst I applaud them for trying to prevent developers from having to reinvent the wheel, I just don't have enough confidence in a pre-alpha library to use it in a production environment. Besides, if you dig around under the hood, you'll see they use BigDecimal too.

Lowry answered 12/11, 2008 at 22:57 Comment(7)
+1. We've decided to add a container class that consumes a currency, too. This comes in handy when rendering monetary values in tables.Atman
yep, that's a pretty common approach and it makes a lot of sense. One caveat to this is when you have to deal with Japanese Yen, as they don't have a minor currency denomination like cents, so it needs it's own rounding rules.Lowry
@Lowry gives a great example of why rolling your own is a bad answer. "Oh, and by the way, it doesn't work for $CURRENCY_X." That's a good sign that it also doesn't work for lots of other currencies.Article
@JamesMoore I disagree that "rolling your own" is a bad approach, you just need to be aware of the possible limitations of the approach that you choose, hence the reason for me mentioning it. It is trivial to implement different rounding rules per currency, but if your system only needs to deal in USD or EUR then you don't need to over engineer things.Lowry
Take a look at #5134737 for just one reason why BigDecimal is a problem. Planet-wide accounting is just a swamp of special cases, and trying to sweep them all under the rug of BigDecimal just doesn't work.Article
I fail to see how this relates to the use of BigDecimal and "rolling your own", surely you will encounter the exact same issues if you use an "off the shelf" solution, especially since the vast majority of third-party libraries use BigDecimal under the hood anyway?Lowry
BigDecimal is slow and uses more memory thus more GC thus even more slow, if you care about speed and you have lot of monetary operations you should use long with precision implementation...Roose
P
52

It can be useful to people arriving here by search engines to know about JodaMoney: http://www.joda.org/joda-money/.

Pronto answered 27/1, 2010 at 11:13 Comment(2)
Thanks. I've been meaning to add a follow-up note about Joda Money. Have you used it?Caveman
+1 looks interesting, glad to see it's BigDecimal under the hood!Lowry
C
21

I'm not expressing my opinion here, but there are quite good arguments against BigDecimal that someone should probably throw out:

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

Clarissa answered 11/10, 2011 at 21:34 Comment(1)
10 years later, BigDecimal won.Stirk
B
8

A convenient library that I ran into earlier is the Joda-Money library. One of its implementations is indeed based on BigDecimal. It is based on the ISO-4217 specification for currencies and can support a customized currency list (loaded via CVS).

This library has a small number of files that one can quickly go through if modifications are needed. Joda-Money is published under the Apache 2.0 license.

Blatman answered 20/9, 2011 at 21:48 Comment(0)
T
7

If you are just using dollars and cents, I'd use a long (offset by 2 decimal places). If you need more detail, big decimal may be the way to go.

Either way, I'd probably extend the class to have a .toString() that uses the correct format, and as a place to put other methods that might come up (For a long, multiplying and dividing will go awry if the decimal isn't adjusted)

Also, if you use define your own class and interface, then you can replace the implementation at will.

Thrombo answered 12/11, 2008 at 22:58 Comment(2)
Watch out, even long could be too short to hold the US Federal Debt in Cents ... if not now then in a few years.Pachalic
I agree--any huge numbers of dollars (or maybe if you are tracking money in Yen) you should be using BigDecimal--but even then I'd seriously consider using a container class for it. I think most programming complexity comes from people not defining small, simple classes around collections and intrinsic types.Thrombo
I
3

BigDecimal or another fixed point representation is what is generally needed for money.

Floating point (Double, Float) representations and calculations are inexact, leading to erroneous results.

Inappreciative answered 12/11, 2008 at 22:57 Comment(3)
Strictly speaking, BigDecimal is also inexact; it just corresponds better with the decimal rounding we're used to in daily life, and allows you to specify rounding modes.Morava
@Michael Borgwardt BigDecimal differs from IEEE FP in that an explicit scale is specified. While not all operations are exact, this ensures that a set of operations and behavior is always exact and the scale is constant whereas the scale for IEEE FP decreases with value.Tavel
What does that have to do with money? Accounting organizations around the world usually have very specific requirements for how you do math in their currency. Does BigDecimal precisely match each and every one of these standards? Will it do so next year, when those standards change? And BigDecimal doesn't even come close to specifying useful rounding rules for currencies.Article
A
2

You have to be so careful when dealing with time and money.

When you are working with money, I hope everybody should know never to use a float or a double.

But I am unsure about BigDecimal.

In most cases you'll be fine if you just keep track of cents in a int or long. This way you never deal with a decimal place.

You only display dollars when you print it. Always work with cents internal using integers. This may be tricky if need to divide or need to use Math.abs().

However, you might care able half a cent, or even one hundredth of a cent. I don't know what's a good way to do this. You might just need to deal with thousandth of cents and use a long. Or maybe you'll be forced to use BigDecimal

I would do a lot more reading on this, but ignore everybody who starts talking about using a float or double to represent money. They are just asking for trouble.

I feel my advice isn't complete, so please put more though into it. You are dealing with dangerous types!

Allantoid answered 13/11, 2008 at 0:34 Comment(4)
Why would you need to be "forced" to use BigDecimal? What are you unsure about? It's clearly superior to working with cents, as it allows you to explicitly specify rounding modes.Morava
@MichaelBorgwardt: yes, it allows you to specify a tiny subset of the rounding modes you need for currencies. So? (Hint: rounding currencies is decided, usually, by national accounting organizations. They're perfectly happy to toss in weird special cases. See #5134737 for just one of the many entertaining reasons why BigDecimal rounding is completely useless here.)Article
@James: How exactly is it "useless"? How would implementing thos special cases be harder with BigDecimal than with something else?Morava
OK, completely useless is too strong. In the complicated class that abstracts currency, BigDecimal's rounding rules are probably useful in some specific instances to build a subset of the ways currency rounding happens. But the general case is that rounding rules for currencies require mechanisms that are subject to change over time (since human accounting agencies make up the rules, and are free to change them). The question isn't about Euros (or whatever replaces Euros next month...), or dollars in 2011, it's about currency, so you have to deal with lots of nasty complexity.Article
F
2

Creating a Money class is the way to go. Using BigDecimal( or even an int) underneath. Then using Currency class to define rounding convention.

Unfortunately without operator overloading Java makes it quite unpleasant creating such basic types.

Fail answered 13/11, 2008 at 3:27 Comment(0)
T
2

There is a better library, timeandmoney. IMO, it far superior to the libraries provided by the JDK for representing these 2 concepts.

Threnody answered 13/11, 2008 at 4:1 Comment(2)
This answer was posted three years ago. Today, the timeandmoney project is still pre-alpha according to that link.Article
@JamesMoore Good call. The answer is now 7 years old and the project is still not stable.Bothersome
S
1

Definitely not BigDecimal. There are so many special rules for rounding and presentation that you have to worry about.

Martin Fowler recommends the implementation of a dedicated Money class to represent currency amounts, and that also implements rules for currency conversion.

Scenery answered 13/11, 2008 at 12:43 Comment(3)
and the underlying data type of his Money class? BigDecimal.Lowry
That's not true. You could use Integer in the money class, which is what Martin does. I have done this many times.Terribly
The recommendation is correct though; calculations involving money are a vast swamp of special cases that change over time. BigDecimal may be useful as a tiny part of the solution, but it's certainly not general.Article
D
1

Hey, here's a very interesting article on BigDecimal, and an illustrative example of why sometimes it is used instead of doubles. BigDecimal Tutorial.

Doughboy answered 14/3, 2011 at 21:39 Comment(0)
A
0

You can use the DecimalFormat class when ultimately displaying a currency value. It provides localization support and is pretty extensible.

Angola answered 13/11, 2008 at 15:40 Comment(0)
J
0

I would encapsulate BigDecimal in Money class that also has a currency just as someone mentioned above. The important thing is that you do an extreme amount of unit tests and especially if working with different currencies. Also it is a good idea if you add a convinient constructor that takes a string or a factory method that does the same so that you can write your tests something like this:

   assertEquals(Money.create("100.0 USD").add("10 GBP"),Money.create("116 USD"));
Jook answered 16/7, 2009 at 13:19 Comment(0)
H
0

There are always constraints and specifics involved. Anyone without sufficient experience to appreciate the subtle issues outlined in the following article should seriously reconsider before dealing with real-world financial data:

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

BigDecimal is hardly the only correct representation or the only piece of the puzzle. Given certain conditions, using a Money class backed by cents stored as an integer could be sufficient and would be much faster than BigDecimal. Yes, that implies the use of dollars as currency and limits amounts but such constraints are perfectly acceptable for many use cases and all currencies have special cases for rounding and sub-denominations anyway, so there's no "universal" solution.

Hardness answered 23/7, 2013 at 17:9 Comment(1)
This seems to be a comment on another post instead of an actual answer. It's also overly vitriolic. Please try to be more civil in the future.Esoteric

© 2022 - 2024 — McMap. All rights reserved.