0

I am getting incorrect result while performing the division using BigDecimal. number1 = 221500.0 number2 = 12.0 Here is my code snippet:

BigDecimal d1 = new BigDecimal(String.valueOf(number1)).setScale(13, 4);
BigDecimal d2 = new BigDecimal(String.valueOf(number2)).setScale(13, 4);
return (d1.divide(d2, 13, 4 )).doubleValue();

18458.333333333332

if I performed the same calculation using the calculator it gives 18458.333333333333333333333333333

Let me know any issues with handling the scale in BigDecimal. It will be helpful if someone let me know how to arrive the same result with calculator.

Suman
  • 21
  • 1
  • 5
  • 1
    It is the correct result. Do you really need such a long result? – T D Nguyen Feb 23 '16 at 23:50
  • The *exact* same result? Does your calculator spit out just the right amount of 3s, or? In reality there's an infinite amount of 3s. – Matti Virkkunen Feb 23 '16 at 23:52
  • 1
    BigDecimal is working exactly how it should be, given that you wanted 13 values. If your purposes require "exact division results" say, for comparison purposes, please multiply by some pre-determined threshold value and compare as ints. In your case, `double` can't hold the value, so the rounding. – Debosmit Ray Feb 23 '16 at 23:54
  • 2
    *"to arrive the same result with calculator"* request 27 digits of precision (not 13), and don't convert to `double`. – Andreas Feb 24 '16 at 00:08

2 Answers2

5

You asked the divide() call for 13 digits of precision, and that is what it gave you.

You then convert that to double and notice that double cannot handle that precision.

BigDecimal d1 = new BigDecimal(String.valueOf(221500.0)).setScale(13, BigDecimal.ROUND_HALF_UP);
BigDecimal d2 = new BigDecimal(String.valueOf(12.0)).setScale(13, BigDecimal.ROUND_HALF_UP);
BigDecimal d3 = d1.divide(d2, 13, BigDecimal.ROUND_HALF_UP);
System.out.println("BigDecimal: " + d3);
System.out.println("as double : " + d3.doubleValue());

OUTPUT

BigDecimal: 18458.3333333333333
as double : 18458.333333333332

If you're going to throw away the extra precision gained by using BigDecimal, why not just do the math in double?


On a different note:
Don't do new BigDecimal(String.valueOf(doubleValue)).
Use BigDecimal.valueOf(doubleValue).

Setting the scale of d1 and d2 seems rather meaningless.
Unless explicitly needed, round as late as possible, if at all.

Andreas
  • 154,647
  • 11
  • 152
  • 247
  • 2
    What @Andreas is saying is, dont call .doubleValue() as doing so will convert your highly precise number into a not so precise double – Arkiliknam Feb 24 '16 at 00:07
3

There are some things you shall do differently.

As you know already, BigDecimal is a great tool since:

  • it can preserve decimal digits exactly, unlike any floating point representation such as double or float

  • it can work with arbitrary big decimal numbers, unlike int, double and float

However to make it working, you shall keep a few things in mind:

  • When setting a value to a BigDecimal, always use a String representation, since only the String representation will handle decimal precision. For example, new BigDecimal(String.valueOf(number1)) is a bad idea, if number is a double (can not handle precise decimals) then you'll covert the unprecise number to a BigDecimal.

  • Do not limit the precision when creating a BigDecimal. Normally a BigDecimal will keep the precision of the input, i.e. new BigDecimal("3.234") will have 3 decimal digits.

  • Do set the roudingMode after each division. Refer to the JavaDoc for the possible rouding modes, the HALF_EVEN is the best pick for usual use cases.

  • Do set the decimal precision at division, since a division may cause endless number of decimals.

Consider this example:

public class BigDecimalTest {

    public static void main(final String[] args) {
        final BigDecimal d1 = new BigDecimal("221500");
        final BigDecimal d2 = new BigDecimal("12");
        final BigDecimal result=d1.divide(d2,30,RoundingMode.HALF_EVEN);
        System.out.println(result);
    }

}

which gives

18458.333333333333333333333333333333

with all the 30 decimals requested. Feel free to change 30 to 300 which gives

18458.333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333

Much better than the calculator, isn't it? :)

Gee Bee
  • 1,794
  • 15
  • 17