DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
  1. DZone
  2. Coding
  3. Java
  4. Working with Money in Java

Working with Money in Java

Markus Eisele user avatar by
Markus Eisele
·
Aug. 19, 11 · Interview
Like (7)
Save
Tweet
Share
62.15K Views

Join the DZone community and get the full member experience.

Join For Free

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

Java (programming language)

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • Tracking Software Architecture Decisions
  • Fargate vs. Lambda: The Battle of the Future
  • Use Golang for Data Processing With Amazon Kinesis and AWS Lambda
  • Top 10 Best Practices for Web Application Testing

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: