Michel Feinstein
Michel Feinstein

Reputation: 14296

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?

Upvotes: 2

Views: 309

Answers (2)

k314159
k314159

Reputation: 11246

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.

Upvotes: 1

Sergey Ponomarev
Sergey Ponomarev

Reputation: 3201

double and especially float types are dangerous. This numbers are base-2 numeral system then it's not possible to represent 0.24F directly as the same it's not possible to represent 1/3 in decimal system without recurring decimal period i.e. 1/3=0.3333... or 0.(3).

So the float number 0.24F when printed back to decimal representation is shown as 0.23 with a change due to rounding:

println(0.24F) => 0.23999999463558197021484375

while 0.25F can be shown directly:

println(0.25F) => 0.25

So answering to your question: it depends. For 0.25, 0.5, 0.75 it's ok to use double.

But the FastMoney class uses not a floating-point arithmetic but a fixed-point arithmetic

Upvotes: 1

Related Questions