These days I had to do some monetary calculations in Java. This reminded me again about primitive `double`

type and that it is absolutely the worst thing you can use when dealing with money. As you know, primitive `double`

don't necessarily give you the right value, only the value that can be stored in a binary number format (any decimal number is represented by an approximated value). Whilst, `java.math.BigDecimal`

gives you the required precision, its performance is very poor comparable to primitive types.

Below are few simple tests that are measuring `BigDecimal`

performance based on a simple computation `x`

which is performed in a loop 250000 times. In order to exclude the JVM warm-up cost, each test was executed 10 times. The execution time was calculated as average of all executions except first execution ^{2} + y^{2}`(exec`

._{2} + exec_{3} + … + exec_{10}) / 9

- Test
`BigDecimal`

computations (execution average time: ~**767 ms**)BigDecimal bd = null; for (double x = 0.5; x <= 500; x += 0.5) { for (double y = 0.5; y <= 500; y += 0.5) { bd = BigDecimal.valueOf(x).pow(2).add(BigDecimal.valueOf(y).pow(2)); } }

We got around**0.003 ms**per computation. Not bad, but wait to see how fast primitive`double`

is. Next test will show us how much time we spend on creation of`BigDecimal`

objects. - Test
`BigDecimal`

constructions (execution average time: ~**468 ms**)for (double x = 0.5; x <= 500; x += 0.5) { for (double y = 0.5; y <= 500; y += 0.5) { BigDecimal.valueOf(x); BigDecimal.valueOf(y); } }

As you can see, only construction of initial operands (`x`

and`y`

) took around**61%**of computation time. We can assume that rest 39% are also spent on`BigDecimal`

object creations (results of`pow()`

and`add()`

operations). Knowing that`BigDecimal`

objects are immutable, let’s see how much we can gain by caching`BigDecimal`

instances. - Test
`BigDecimal`

computations with cache (execution average time: ~**309 ms**)BigDecimal bd = null; for (double x = 0.5; x <= 500; x += 0.5) { for (double y = 0.5; y <= 500; y += 0.5) { BigDecimal bigDecimal1 = cache.get(x); BigDecimal bigDecimal2 = cache.get(y); bd = bigDecimal1.pow(2).add(bigDecimal2.pow(2)); } }

Cache was prepared as follows:Map<Double, BigDecimal> cache = new HashMap<Double, BigDecimal>(); for (double x = 0.5; x <= 500; x += 0.5) { cache.put(x, BigDecimal.valueOf(x)); }

As you can see, caching`BigDecimal`

gives a performance boost. Now it’s time to see how the primitive`double`

type is performing. - Test
`double`

computations (execution average time: ~**2 ms**)double d = 0.0; for (double x = 0.5; x <= 500; x += 0.5) { for (double y = 0.5; y <= 500; y += 0.5) { d = x * x + y * y; } }

Computation speed is great but unfortunately floating point numbers cannot be used for monetary calculations.

**Performance Improvement to BigDecimal**

Java SE 6 Update 25 made some performance improvements for `BigDecimal`

. Following is the excerpt from Java SE 6 Update 25 Release Notes:

`Improvements have been made to class BigDecimal enhancing its performance by thirty percent. BigDecimal is enabled by specifying ``-XX:+AggressiveOpts`

command option.

I repeated all the above tests with `-XX:+AggressiveOpts`

option. Below is the table with results:

Test | Average Exec Time | Performance Boost | |

Normal | -XX:+AggressiveOpts | ||

`BigDecimal` computations | 767 ms | 644 ms | 16% |

`BigDecimal` constructions | 468 ms | 394 ms | 16% |

`BigDecimal` computations with cache | 309 ms | 309 ms | 0% |

`double` computations | 2 ms | - | - |

Results speak themselves, `-XX:+AggressiveOpts`

option gave us **16%** performance improvement. It’s not a 30% improvement but still it is something, if we take into account that we just added a JVM option. The most interesting thing is that we got 0% performance improvement for the test which uses cache and the performance improvement of `BigDecimal`

computations test and `BigDecimal`

constructions test are the same. This leads me to think that improvements were made mostly for `BigDecimal`

objects creation.

Regarding `BigDecimal`

I would like also to remind you about:

- Never use
`BigDecimal(double)`

constructor. The recommended constructor is`BigDecimal(String)`

, or in case you want to convert an existing double into a`BigDecimal`

you can use`BigDecimal.valueOf(double)`

static method. - When dividing always specify a
`RoundingMode`

. Instead of`divide(BigDecimal)`

use`divide(BigDecimal, RoundingMode)`

. This has to be done in order to avoid`ArithmeticException`

. Below is an excerpt from`BigDecimal`

javadoc:In the case of divide, the exact quotient could have an infinitely long decimal expansion; for example, 1 divided by 3. If the quotient has a nonterminating decimal expansion and the operation is specified to return an exact result, an

`ArithmeticException`

is thrown.

At the end, I want to say that for my task I used neither `double`

nor `BigDecimal`

. I used primitive `long`

type and I stored money as cents, pennies (or the equivalent, of course). The choice was done because of performance reasons. I had to do many very simple computations but very often.

** Update (05-Aug-2011):** Be aware of bug, recently discovered in Java 7 which is also applicable for Java 6 in case when the following JVM options are used:

`-XX:+OptimizeStringConcat`

or `-XX:+AggressiveOpts`

. More information here: Java7 Hotspot Loop Bug Details.** Update (01-Mar-2012):** Bug was fixed in Java 6 update 29 and Java 7 update 1.

Wow! this is Amazing! Do you know your hidden name meaning ? Click here to find your hidden name meaning

ReplyDelete