Markus is a principal technology consultant working for msg systems ag in Germany. Markus is a software architect, developer and consultant. He also writes for IT magazines. Markus joined msg in 2002 and has been a member of the Center of Competence IT-Architecture for nine years. After that Markus moved on to the IT-Strategy and Architecture group. He works daily with customers and projects dealing with Enterprise level Java and infrastructures. This includes the Java platform and several Web-related technologies on a variety of platforms using products from different vendors. His main area of expertise are Java EE Servers. Markus is speaking at different conferences about his favorite topics. He is also part of the Java EE 7 expert group. Stay up to date with his activities visiting his blog (http://blog.eisele.net/). Follow him on twitter @myfear. Markus is a DZone MVB and is not an employee of DZone and has posted 159 posts at DZone. You can read more from them at their website. View Full User Profile

Working with Money in Java

08.19.2011
| 26122 views |
  • submit to reddit

I've a new favorite blog. Peter Lawrey is running a blog called "Vanilla Java". It's all about "Understanding how Core Java really works". Peter is a DZone MVB and pushing some posts about speed and size of different objects and technologies in Java. One particular post recently caught my attention and made me think.

 

Picture: Images_of_Money (CC BY 2.0) (TaxBrackets.org)

It is "Double your money again" in which Peter talks about rounding problems while using java.lang.Double and it's primitive type double. This is a field I know a little about and have seen a lot. And I thought I share a bit about my experiences here.

If you are using double you run into trouble
Mainly because of the fact, that double (Wrapper: Double) is a double-precision 64-bit IEEE 754 floating point. It's not meant for keeping exact decimal values.
double result = 0.1 + 0.2 - 0.3;
System.out.println("0.1 + 0.2 - 0.3=" + result);
=> 0.1 + 0.2 - 0.3=5.551115123125783E-17

The .toString() representation follows some (not so simple) rules:
If m is greater than or equal to 10-3 but less than 107, then it is represented as the integer part of m, in decimal form with no leading zeroes, followed by '.' ('\u002E'), followed by one or more decimal digits representing the fractional part of m.
If m is less than 10-3 or greater than or equal to 107, then it is represented in so-called "computerized scientific notation." Let n be the unique integer such that 10n ≤ m < 10n+1; then let a be the mathematically exact quotient of m and 10n so that 1 ≤ a < 10. The magnitude is then represented as the integer part of a, as a single decimal digit, followed by '.' ('\u002E'), followed by decimal digits representing the fractional part of a, followed by the letter 'E' ('\u0045'), followed by a representation of n as a decimal integer, as produced by the method Integer.toString(int).
(Source: java.lang.Double)

Following this: you run into trouble with presentation and calculations which you have to handle.

Rounding Tie-breaking
Back to Peter's initial post. He is proposing to use a rounding algorithm when working with doubles to prevent undetermined effects. The little methods he shows are nice: But Java already knows about rounding and further on, it already knows about more than one rounding algorithm. Now it's up to you to choose the best one. We have a couple of them at hand (compare wikipedia article). Let's go:

ROUND_CEILING
Rounding mode to round towards positive infinity.
ROUND_DOWN 
Rounding mode to round towards zero.
ROUND_FLOOR 
Rounding mode to round towards negative infinity.
ROUND_HALF_DOWN 
Rounding mode to round towards "nearest neighbor" unless both neighbors are equidistant, in which case round down.
ROUND_HALF_EVEN 
Rounding mode to round towards the "nearest neighbor" unless both neighbors are equidistant, in which case, round towards the even neighbor.
ROUND_HALF_UP 
Rounding mode to round towards "nearest neighbor" unless both neighbors are equidistant, in which case round up.
ROUND_UP 
Rounding mode to round away from zero.

To get the results processed with the so-called Gaussian- , or bankers' rounding all you have to do is to set a scale on your BigDecimal.

double result = 0.1 + 0.2 - 0.3;
BigDecimal resultRounded = new BigDecimal(result).setScale(2, BigDecimal.ROUND_HALF_UP);
System.out.println("0.1 + 0.2 - 0.3=" + resultRounded);
 => 0.1 + 0.2 - 0.3=0.00


There is some conversion involved here. As you can see, I'm converting the double to a BigDecimal. That means, you cannot use it for further calculations. if you need a double you probably can do the following:

double result = new BigDecimal(value).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue(); 

But as Peter states in the comments on his post this is probably not performing very well.

BigDecimal != Amount
Even if you now know how to get rid of the rounding problem you still have a simple number at hand and not an amount. Which means, you have to fix the presentation. What should be simple, isn't. As always you have a couple of approaches to evaluate.

DecimalFormat
The preferred way is to use the DecimalFormat class to format decimal numbers into locale-specific strings. This class allows you to control the display of leading and trailing zeros, prefixes and suffixes, grouping (thousands) separators, and the decimal separator. You can grep an instance by using the NumberFormat.getCurrencyInstance(locale)

double result = 0.1 + 0.2 - 0.3;
BigDecimal result2 = new BigDecimal(result).setScale(2, BigDecimal.ROUND_HALF_UP);
NumberFormat form = NumberFormat.getCurrencyInstance(new Locale("de", "DE"));
System.out.println("Amount: " + form.format(result2));
 => Amount: 0,00 €


Currency
If you need more control than you already have with the preferred way, you could think about using the Currency class. Look at java.util.Currency as it represents a currency identified by it's ISO 4217 currency code. Another alternative is to get it with a locale.

Currency curr = Currency.getInstance("EUR");
Currency currLoc = Currency.getInstance(new Locale("de", "DE"));
System.out.println("currency EUR in en_US: "+curr.getSymbol(new Locale("de", "DE")));
System.out.println("currency in de_DE for en_US: "+currLoc.getSymbol(new Locale("en", "US")));
 =>currency EUR in en_US: €
 =>currency in de_DE for en_US: EUR



Bottom line
If you have a chance to, go ahead with the build in functionality. Both the rounding issues as the i18n stuff can be addressed with the set of utilities and classes available. If you tend to have your own Amount class using the custom currency mechanisms be aware of the fact, that you have to place the currency symbol in front or afterwards depending on the locale you are in. So: There is not much magic inside and if you use it the right way, you don't have to fear working with money in Java at all.

 

From http://blog.eisele.net/2011/08/working-with-money-in-java.html

Published at DZone with permission of Markus Eisele, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Tags:

Comments

Dan Macbean replied on Fri, 2011/08/19 - 3:18am

I was always told to just use BigDecimal when dealing with money. Why would you use double instead of BigDecimal when doing calculations for currency values ? Is it purely a performance issue?

Jeff Peff replied on Fri, 2011/08/19 - 4:13am in response to: Dan Macbean

Agreed, 've been told the same thing.

David Burkhart replied on Fri, 2011/08/19 - 6:28am

Independent of wheter we want to use double or BigDecimal we should to encapsulate these details in a Money-class. So if you use money in your application do not use double or BigDecimal directly. Use 'Money'. And write some unit tests to document rounding and other aspects our customers care about. But I know this is not what the articles are about, so this is no criticism but more an additional comment.

Manuel Jordan replied on Fri, 2011/08/19 - 8:57am

Interesting work, thank you

Artur Biesiadowski replied on Fri, 2011/08/19 - 9:15am

Performance issue is one thing. Other is that as soon as you start doing something less trivial, like computing option values, you anyway go into area of being non-exact with results. Often the inputs are rounded to 5-6 digits, then you do pow/log over and over using tens of pre-rounded and inexact values. You get the number, which you are later supposed to round to something like xx.yyy for sending to market. Pretending that 17 digits of precision is not enough for you in this case makes no sense.

And on top of that, financials models are anyway rough - it is important that they give consistent results, there is no such thing as being really 'correct'.

Just remember to always format the output and compare abs(x) < epsilon rather than x == 0 and you will be ok ;)

Real problem I have seen in one of the financial packages was applying those rules inconsistently - program was displaying risk for portfolio in formatted way. Traders have hedged themselves to the exact value displayed, but after that position could not be closed - because program was checking for 0, while there was still 0.00000016 EUR risk outstanding in the book... Fix was to ignore the risks/positions less than 1/1000 of the smallest unit of currency and everything worked properly (and fast).

Michael Fortin replied on Fri, 2011/08/19 - 9:19am

aah, what's wrong with using cents instead of dollars and calculate everything using long. I could see why you'd need BigDecimal if you need currency values above Long.MAX_VALUE/100 but if you don't why use it?

Ludovic Hochet replied on Sat, 2011/08/20 - 7:34am

what about Joda Money (http://joda-money.sourceforge.net/) ?

Manuel Jordan replied on Sat, 2011/08/20 - 11:03am in response to: Ludovic Hochet

Thanks for share this valuable link!

Jonathan Fisher replied on Sun, 2011/08/21 - 2:02am

BigDecimal is great, but it's pretty danged slow. A solution I've heard, but never used, is to use long and represent the amounts as pennies. If you cut a penny in half, it won't be missed too much by most systems. I suppose there's times when you need absolute precision though... everything in context.

Peter Lawrey replied on Sun, 2011/08/21 - 2:36am

There are project which use one of BigDecimal, double or int/long for money. Each has pluses and minuses.  Each approach is valid in the right situation.

One advantage of double is it supports NaN, which can be used for stale/invalid prices. It can also be slightly faster than long, but that depends on your CPU. (This is one reason int can be used instead of long)

A problem with using Money (or any Object) is again performance, however if its not critical, this is a better approach as it is less likely to be error prone.

Personally I like being about to write simple expressions x * y / z which I find clearer, however it has been noted that Scala allows you to use operators for BigDecimal and I believe it has been considered for Java in the future. Scala has also made significant improvement in performance as I can imagine it beging used more and more for financial applications.

Liam Knox replied on Sun, 2011/08/21 - 6:33am in response to: Peter Lawrey

Totally agree. BigDecimal should support the same number primitive semantics in Java. It is a number after all and the a.add(b) when you add the immutability is just a purely crap design that can lead to massive holes

I am not sure exactly what Scala has done relative to Java in making this more optimized though?

Werner Keil replied on Sat, 2013/01/05 - 11:41am in response to: Jonathan Fisher

JodaMoney uses BigDecimal all over the place.Also hasn't been maintained for ages, but some inspiration was taken by it and other popular projects for JSR 354, Java Money and Currency API 

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.