Search code examples
precisionjava-moneyjsr354

Are there any caveats of creating a FastMoney/Money from a double?


I have seen there are issues when creating BigDecimal from double.

Although FastMoney doesn't use BigDecimal (as opposed to Money), I am not sure how either of them will behave when I crate them from a double value.

Is creating a FastMoney/Money instance from a double not recommended? Should I always try to create them from String?


Solution

  • The floating-point types float and double are dangerous to use in monetary calculations, but when used to create a JSR-354 MonetaryAmount, you will be lucky enough:

    First, MonetaryAmount (or at least the reference implementation of FastMoney, which seems to be what most users prefer) only supports 5 decimal places. If you try to convert a double such as 0.123456, you'll get an ArithmeticException.

    If you try to convert a double that represents a normal typical amount with 2dp, such as FastMoney.of(1.23, "USD"), this is what will happen:

    1. Your double argument will be boxed into a Double and the method FastMoney.of(Double, String) will be called.

    2. This method will convert your number to a long value 123000 (representing 1.23000) by calling BigDecimal.valueOf(1.23).

    3. BigDecimal.valueOf(1.23) will use the string from Double.toString(1.23).

    4. Double.toString(1.23) will convert that into "1.23" using the complex rules described in the Javadoc

    5. It so happens that all double numbers 0.00001, 0.00002, ..., 0.99999 get converted to String (and thereby to BigDecimal) as exact decimals using this method.

    So, to summarise, you can use double to create a FastMoney amount, but you should avoid it.