Overview
There is a common belief that you cannot use double for money due to floating point error. The work around for this is to use BigDecimal. However, and anything other than simple functions like add and minus, round is still required.
One reason not to use BigDecimal is the complexity it add to the code, while given little advantage over simpler double numbers.
Myth: double cannot be used for money.
Money is a limited resource in most currencies. This means the maximum number of significant figures is also limited. Take for instance a big number, the USA national deficit. It was $12,099,333,980,260.38 http://www.brillig.com/debt_clock/
If you convert this to BigDecimal and print it out you can see how this value is actually represented.
// prints 12990333980260.380859375 System.out.println(new BigDecimal(12990333980260.38));
So in this case, the error is 0.00859375. This is because double is only accurate to about 16 decimal places. Is this accurate enough? If I check the debt clock again the national debt is now $12,099,359,560,853.48 i.e. it is now 26 million more so I could reasonably make the judgement call that a fraction of a cent is not important and can be rounded off.
| It is important to get agreement on what a reason error/rounding is. This is usually the smallest denomination but may be one tenth, one hundredth or one thousandth of the smallest denomination. |
In summary.
Money usually requires between 8 and 12 digits of accuracy. double is accurate to 15 or 16 decimal places and this is usually more than enough.
A simple way to round half.
In this approach, a double is converted to a long after multiplying by the appropriate power of 10.
private static long TENS[] = new long[19]; static { TENS[0] = 1; for (int i = 1; i < TENS.length; i++) TENS[i] = 10 * TENS[i - 1]; } public static double round(double v, int precision) { assert precision >= 0 && precision < TENS.length; double unscaled = v * TENS[precision]; assert unscaled > Long.MIN_VALUE && unscaled < Long.MAX_VALUE; long unscaledLong = (long) (unscaled + (v < 0 ? -0.5 : 0.5)); return (double) unscaledLong / TENS[precision]; }
Using a present array of long values is much faster and more accurate than using Math.pow(10, x)
This function is as much as 100x faster than using the equivalent function is in BigInteger.
However, the most important reason to use double is that the code is easier to read and maintain.
Add Comment