Jefferson
Jefferson

Reputation: 190

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?

Upvotes: 15

Views: 29362

Answers (5)

Peter Lawrey
Peter Lawrey

Reputation: 533472

The problem you have is that 0.1 is represented with a slightly higher number e.g.

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

prints

0.1000000000000000055511151231257827021181583404541015625

The Double.toString() takes into account this representation error so you don't see it.

Similarly 0.3 is represented by a value slightly lower than it really is.

0.299999999999999988897769753748434595763683319091796875

If you multiply the represented value of 0.1 by 3 you don't get the represented value for 0.3, you instead get something a little higher

0.3000000000000000166533453693773481063544750213623046875

This is not just a representation error but also a rounding error caused by the operations. This is more than the Double.toString() will correct and so you see the rounding error.

The moral of the story, if you use float or double also round the solution appropriately.

double d = 0.1 + 0.1 + 0.1;
System.out.println(d);
double d2 = (long)(d * 1e6 + 0.5) / 1e6; // round to 6 decimal places.
System.out.println(d2);

prints

0.30000000000000004
0.3

Upvotes: 3

Voo
Voo

Reputation: 30206

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.

Upvotes: 11

Louis Wasserman
Louis Wasserman

Reputation: 198023

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.

Upvotes: 32

James Cronen
James Cronen

Reputation: 5763

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);

Upvotes: 3

Jakub Zaverka
Jakub Zaverka

Reputation: 8874

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.

Upvotes: 6

Related Questions