## Monetary Calculation Pitfalls

People expect computer programs to be accurate and precise when calculating numbers, and especially programs that perform monetary calculations and as a Java developer, sooner or later you will need to deal with monetary calculations.

You may be tempted to use the primitive types of `float`

or `double`

for non-integer numbers. Both of these types support numbers with decimals. As we will see below, there are some significant pitfalls in using these primitive types for monetary calculations.

## Monetary Calculations: Bad Examples

You have 1.55 $ and you bought a candy worth 35 ¢. How much is left with you?

Let’s do the calculation in Java.

1 2 3 4 5 |
. . . float pocketMoney=1.55f; float price=.35f; System.out.println(pocketMoney - price); . . . |

If you said 1.20 $, you are in for a surprise. The output is:

1 |
1.1999999 |

Let us look at another example, this time using `double`

. A product is priced 10 $. What is the price after adding 8.25 % tax to it?

1 2 3 4 5 6 7 8 |
. . . double amount = 10.00; double tax = .0825; double taxAmount = amount * tax; double amountAfterTax = amount + taxAmount; System.out.println("Tax: "+ taxAmount); System.out.println("Amount after tax: " + amountAfterTax); . . . |

The output is:

1 2 |
Tax: 0.8250000000000001 Amount after tax: 10.825 |

We again have some unexpected numbers. A simple multiplication “10.00 X .0825” is not giving us what we expected “0.825”.

So what’s the problem? Why can’t Java perform such simple calculations? The problem is not with Java but how floating point is defined in the IEEE 754 standard based on which Java implements `float`

and `double`

. Floating points are designed to provide accurate approximations (**but not exact results**) quickly and it is impossible to exactly represent 0.1 (or any other negative power of ten) as a Java `float`

or `double`

.

Without going into more details of binary floating point arithmetic, let’s find out how to perform monetary calculations accurately in Java. A solution is to do everything using integral types (`int`

and `long`

) and I have met several programmers advocating it. But then you will need to remember that “325” in your program is really “3.25” dollar. Also, how will you do percent calculations rounding to the nearest cent? This is when you should turn to the BigDecimal class.

## Monetary Calculations Using BigDecimal

The `BigDecimal`

class is a part of the `java.math`

package. For a decimal number, `BigDecimal`

internally stores the unscaled value in a `BigInteger`

and the decimal scale (digits to the right of the decimal point) in an `integer`

. So, the internally used `BigInteger`

allows `BigDecimal`

to represent any number, however big it is (only limited to physical memory) and the integer allows accurate handling of the decimal scale.

During addition and subtraction operations, `BigDecimal`

expands the number with the smallest scale before performing the operation. This guarantees that the sum or difference is exact to the last digit. During multiplication, `BigDecimal`

computes the sum of the numbers scale and based on it expands its decimal scale. For division, `BigDecimal`

expects that the result can be represented with a scale that is the difference between the scale of the dividend and divisor.

To perform the arithmetic calculations, `BigDecimal`

provides the `add()`

, `subtract()`

, `multiply()`

, and `divide()`

methods. Before we use these methods, we need to represent the numbers as `BigDecimal`

objects. The` BigDecimal`

class contains 16 overloaded constructors but the one that you will use to represent a monetary value is `BigDecimal(String val)`

. This is important because if you mistakenly use the one that accepts `double`

, you will face same issues as you do while using `float`

and `double`

. This happens because the `float`

or `double`

parameter value will undergo loss of precision before you pass them to the constructor. On the other hand, when you use the `String`

constructor, `BigDecimal`

will represent exactly the number you pass to it.

Let’s now perform some `BigDecimal`

calculations.

1 2 3 4 5 6 7 8 |
. . . BigDecimal num1 = new BigDecimal("2.5"); BigDecimal num2 = new BigDecimal("3.5"); System.out.println("BigDecimal Addition: "+ num1.add(num2)); System.out.println("BigDecimal Subtraction: "+ num1.subtract(num2)); System.out.println("BigDecimal Multiplication: "+ num1.multiply(num2)); System.out.println("BigDecimal Division: "+ num1.divide(num2)); . . . |

In the example above, we created two `BigDecimal`

numbers and called the the `add()`

, `subtract()`

, `multiply()`

, and `divide()`

methods to perform arithmetic calculations.

The output is:

1 2 3 4 5 6 |
BigDecimal Addition: 6.0 BigDecimal Subtraction: -1.0 BigDecimal Multiplication: 8.75 Exception in thread "main" java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result. at java.math.BigDecimal.divide(BigDecimal.java:1690) at prototype.CloneExample.main(CloneExample.java:24) |

As we can see the addition, subtraction, and multiplication in lines 4-6 were done as expected but the division in line 7 resulted in an exception. This happened because what we have is a non-terminating decimal expansion “2.5/3.5 = 0.7142857142857. . . . .”. As mentioned earlier, for division `BigDecimal`

expects that the result can be represented with a scale that is the difference between the scale of the dividend and divisor. Else, the JavaDoc says *“. . .if the exact quotient cannot be represented (because it has a non-terminating decimal expansion) an ArithmeticException is thrown”*– and hence the exception thrown in line 7.

To avoid such exceptions, always set a resulting scale and a rounding mode during division by using the overloaded `divide(BigDecimal divisor, int scale, RoundingMode roundingMode)`

method. In this method, the first parameter is the `BigDecimal`

divisor. The second parameter specifies the decimal scale and the third an enumeration value of the `RoundingMode`

enum. This enum, introduced in Java SE 6, provides eight different types of rounding modes whose descriptions with examples are available here.

**Note**: `BigDecimal`

itself have integer fields to represent rounding modes, but they are now obsolete. Ensure that you instead use the `RoundingMode`

enum for rounding modes in calculations.

To display BigDecimal numbers, you can set the scale and rounding mode with the `BigDecimal.setScale(scale, roundingMode)`

method.

Now, the question is- Which rounding mode should I use for monetary calculationd? The answer is, there is “no specific” mode. It depends on the application requirements and any legal contracts that your application must conform with. For example, it is common to use `RoundingMode.HALF_UP`

for tax calculations. If you are selling a product and want to round in favor of the customer, use `RoundingMode.CEILING`

. If you are not sure, go for `RoundingMode.HALF_EVEN`

. This rounding mode, also known as *“Banker’s rounding”*, when applied repeatedly over a sequence of calculations statistically minimizes cumulative error.

Let’s now write some code to perform some monetary calculations using `BigDecimal`

.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
package guru.springframework.blog.monetarycalculations; import java.math.BigDecimal; import java.math.RoundingMode; public class BigDecimalCalc { public void calculate(String param1, String param2){ System.out.println("--------------------calculate-----------------------"); BigDecimal num1=new BigDecimal(param1); BigDecimal num2=new BigDecimal(param2); System.out.println("num1: "+num1+" num2: "+ num2); System.out.println("BigDecimal Addition: "+num1.add(num2)); System.out.println("BigDecimal Subtraction: " + num1.subtract(num2)); System.out.println("BigDecimal Multiplication: "+num1.multiply(num2)); } public void divideWithScaleRounding(String param1, String param2){ System.out.println("--------------------divisionWithScaleRounding-----------------------"); /*Setting scale and rounding mode for division using overloaded divide(BigDecimal divisor, int scale, RoundingMode roundingMode) */ BigDecimal num1=new BigDecimal(param1); BigDecimal num2=new BigDecimal(param2); System.out.println("num1: "+num1+" num2: "+ num2); System.out.println("BigDecimal Division with overloaded divide(): " + num1.divide(num2, 4, RoundingMode.HALF_EVEN)); } public void calculateTax(String amount, String tax){ System.out.println("--------------------calculateTax-----------------------"); BigDecimal bdAmount = new BigDecimal(amount); BigDecimal bdTax = new BigDecimal(tax); BigDecimal taxAmount = bdAmount.multiply(bdTax); /*Setting scale and rounding mode using setScale() */ taxAmount = taxAmount.setScale(2, RoundingMode.HALF_UP); BigDecimal finalAmount = bdAmount.add(taxAmount); finalAmount = finalAmount.setScale(2, RoundingMode.HALF_UP); System.out.println("Amount : " + bdAmount); System.out.println("Tax : " + taxAmount); System.out.println("Amount after tax: " + finalAmount); } } |

In the example above, we first wrote a `calculate()`

method that accepts two `String`

parameters. In lines 13-14 we converted them into `BigDecimal`

. In lines 16-18 we performed addition, subtraction, and multiplication operations on the numbers. Next, we wrote a `divideWithScaleRounding()`

method that also accepts two `String`

parameters that we converted to `BigDecimal`

in line 26-27. In line 29, we performed a division with a scale 4 and a rounding mode, `RoundingMode.HALF_EVEN`

. Then, we wrote a `calculateTax()`

method that accepts a monetary amount and a tax as `String`

objects. After converting the parameters to `BigDecimal`

, we calculated the tax amount in line 36. To display the tax amount with a scale 2 and a rounding mode `RoundingMode.HALF_UP`

, we called the `setScale()`

method in line 38. Similarly, we calculated the final amount and set its scale and rounding mode in lines 39-40. To test our example, let’s write this unit test.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
package guru.springframework.blog.monetarycalculations; import org.junit.Test; public class BigDecimalCalcTest { @Test public void testCalculate() throws Exception { new BigDecimalCalc().calculate("4.0", "2.0"); } @Test public void testDivideWithScaleRounding() throws Exception { new BigDecimalCalc().divideWithScaleRounding("2.5", "3.5"); } @Test public void testCalculateTax() throws Exception { new BigDecimalCalc().calculateTax("10.00", ".0825"); } } |

The output is:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
. ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v1.2.3.RELEASE) Running guru.springframework.blog.monetarycalculations.BigDecimalCalcTest --------------------divisionWithScaleRounding----------------------- num1: 2.5 num2: 3.5 BigDecimal Division with overloaded divide(): 0.7143 --------------------calculate----------------------- num1: 4.0 num2: 2.0 BigDecimal Addition: 6.0 BigDecimal Subtraction: 2.0 BigDecimal Multiplication: 8.00 --------------------calculateTax----------------------- Amount : 10.00 Tax : 0.83 Amount after tax: 10.83 Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.011 sec - in guru.springframework.blog.monetarycalculations.BigDecimalCalcTest |

In the output above, observe how `BigDecimal`

performed the division and displayed monetary amounts with the specified scale and rounding mode. Using this example, go ahead and experiment with different sets of monetary values, scales, and rounding modes.

## Summary

While float and double are natural choices for scientific, statistical and engineering calculations, BigDecimal is the ideal choice when it comes to monetary calculations. But, the precision and accuracy of BigDecimal comes with a price- and it is performance. However, if we require completely accurate monetary calculations, sacrifice of some performance is acceptable.

On an ending note, keep an eye on JSR 354: Money and Currency API, which is planned to be included in Java 9. A part of this API is designed to support complex monetary calculation rules including calculation and display precisions. It will be interesting to see if this API could change how we count our money in future.

## Get The Code

I’ve committed the source code for this post to github. It is a Maven project which you can download and build. If you wish to learn more about the Spring Framework, I have a free introduction to Spring tutorial. You can sign up for this tutorial in the section below.

AshokonUsing YAML in Spring Boot to Configure Logback