Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java BigDecimal precision problems

I know the following behavior is an old problem, but still I don't understand.

System.out.println(0.1 + 0.1 + 0.1);    

Or even though I use BigDecimal

System.out.println(new BigDecimal(0.1).doubleValue()
    + new BigDecimal(0.1).doubleValue()
    + new BigDecimal(0.1).doubleValue());

Why this result is: 0.30000000000000004 instead of: 0.3?

How can I solve this?

like image 257
Jefferson Avatar asked Mar 20 '12 21:03

Jefferson


4 Answers

What you actually want is

new BigDecimal("0.1")
 .add(new BigDecimal("0.1"))
 .add(new BigDecimal("0.1"));

The new BigDecimal(double) constructor gets all the imprecision of the double, so by the time you've said 0.1, you've already introduced the rounding error. Using the String constructor avoids the rounding error associated with going via the double.

like image 142
Louis Wasserman Avatar answered Sep 27 '22 20:09

Louis Wasserman


First never, never use the double constructor of BigDecimal. It may be the right thing in a few situations but mostly it isn't

If you can control your input use the BigDecimal String constructor as was already proposed. That way you get exactly what you want. If you already have a double (can happen after all), don't use the double constructor but instead the static valueOf method. That has the nice advantage that we get the cannonical representation of the double which mitigates the problem at least.. and the result is usually much more intuitive.

like image 45
Voo Avatar answered Sep 27 '22 20:09

Voo


This is not a problem of Java, but rather a problem of computers generally. The core problem lies in the conversion from decimal format (human format) to binary format (computer format). Some numbers in decimal format are not representable in binary format without infinite repeating decimals.

For example, 0.3 decimal is 0.01001100... binary But a computer has a limited "slots" (bits) to save a number, so it cannot save all the whole infinite representation. It saves only 0.01001100110011001100 (for example). But that number in decimal is no longer 0.3, but 0.30000000000000004 instead.

like image 27
Jakub Zaverka Avatar answered Sep 27 '22 20:09

Jakub Zaverka


Try this:

BigDecimal sum = new BigDecimal(0.1).add(new BigDecimal(0.1)).add(new BigDecimal(0.1));

EDIT: Actually, looking over the Javadoc, this will have the same problem as the original. The constructor BigDecimal(double) will make a BigDecimal corresponding to the exact floating-point representation of 0.1, which is not exactly equal to 0.1.

This, however, gives the exact result, since integers CAN always be expressed exactly in floating-point representation:

BigDecimal one = new BigDecimal(1);
BigDecimal oneTenth = one.divide(new BigDecimal(10));

BigDecimal sum = oneTenth.add(oneTenth).add(oneTenth);
like image 26
James Cronen Avatar answered Sep 27 '22 18:09

James Cronen