6

Possible Duplicate:
ArithmeticException thrown during BigDecimal.divide

This results in ArithmeticException: http://ideone.com/RXeZw

Providing a scale and rounding mode will give me the wrong result. This example should output 50.03%. How to round this correctly?

For easier reference, this is the code:

BigDecimal priceDiff = BigDecimal.ONE
     .subtract(new BigDecimal(9.99)
     .divide(new BigDecimal(19.99)))
     .multiply(new BigDecimal(100));
System.out.println(priceDiff.toPlainString());
Community
  • 1
  • 1
dtrunk
  • 4,685
  • 17
  • 65
  • 109

5 Answers5

10

BigDecimal.divide(BigDecimal divisor) throws an ArithmeticException if the result cannot be exactly represented.
You will have to use provide a MathContext or a RoundingMode telling how you want to handle this. For example:

public class Test {
    public static void main(String[] args) throws java.lang.Exception {
        BigDecimal priceDiff = BigDecimal.ONE.subtract(new BigDecimal("9.99").divide(new BigDecimal("19.99"), MathContext.DECIMAL128))
                                             .multiply(new BigDecimal(100));
        System.out.println(priceDiff.toPlainString());
    }
}

works and prints

50.0250125062531265632816408204102100

Also, note the use of the BigDecimal(String) constructor to avoid problems when you create BigDecimal using a double literal.

obataku
  • 29,212
  • 3
  • 44
  • 57
Keppil
  • 45,603
  • 8
  • 97
  • 119
4

You should use a MathContext as your division as an infinite number of decimals:

    BigDecimal priceDiff = BigDecimal.ONE
            .subtract(new BigDecimal(9.99)
            .divide(new BigDecimal(19.99), new MathContext(10, RoundingMode.UP)))
            .multiply(new BigDecimal(100));
    System.out.println(priceDiff.toPlainString());

However, that prints 50.025...

This would print 49.98:

BigDecimal priceDiff = new BigDecimal(9.99)
        .divide(new BigDecimal(19.99), new MathContext(10, RoundingMode.UP))
        .multiply(new BigDecimal(100), new MathContext(4, RoundingMode.UP));
System.out.println(priceDiff.toPlainString());
assylias
  • 321,522
  • 82
  • 660
  • 783
2

From the Javadoc:

In the case of divide, the exact quotient could have an infinitely long decimal expansion; for example, 1 divided by 3. If the quotient has a nonterminating decimal expansion and the operation is specified to return an exact result, an ArithmeticException is thrown.

The solution is to provide a MathContext to the division, for example MathContext.DECIMAL128. If you want rounding done the conventional way (rounding halves up rather than to the nearest even number), use MathContext(int precision)

You should also be creating your BigDecimals with String arguments rather than double arguments, because the latter causes loss of precision.

Jacob Raihle
  • 3,720
  • 18
  • 34
1

BigDecimal operations always tries to calculate exact result. Try this.

 BigDecimal priceDiff = BigDecimal.ONE
                .subtract(new BigDecimal(9.99)
                        .divide(new BigDecimal(19.99),RoundingMode.HALF_UP))
                .multiply(new BigDecimal(100));
        System.out.println(priceDiff.toPlainString());
erencan
  • 3,725
  • 5
  • 32
  • 50
1

Similar to @Keppil's answer

double d = (1 - 9.99 / 19.99) * 100;
System.out.printf("%.2f%%%n", d);

prints

50.03%

You are not going to get 49.98% as this is not the answer to your equation.

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130