Search code examples
javaroundingbigdecimaldivide

BigDecimal 1.0E+8 / 100000000 with ROUND_HALF_UP is 0


Can some one explain why I get 0 as result for the first line?

System.out.println((new BigDecimal("1.0E+8")).divide(new BigDecimal(100000000), BigDecimal.ROUND_HALF_UP));
System.out.println((new BigDecimal("1.0E+8")).subtract(BigDecimal.ONE).divide(new BigDecimal(100000000), BigDecimal.ROUND_HALF_UP));

0E+7
1

Solution

  • It all has to do with the scales of the BigDecimals involved.

    When you specify exponential format in the constructor that takes a String, the scale may be negative. This indicates that the significant digits don't extend all the way down to unity.

    BigDecimal oneEPlus8 = new BigDecimal("1.0E+8");
    System.out.println(oneEPlus8.scale());
    

    This outputs -7.

    Using the constructor that takes an int yields a scale of 0.

    BigDecimal oneHundredMillion = new BigDecimal(100000000);
    System.out.println(oneHundredMillion.scale());
    

    This outputs 0.

    When you divide BigDecimals, the quotient takes the scale of the object on which divide is called. So, the quotient's scale is also -7. The result was calculated to be 1, but in the scale of -7, rounding only gives two options: 0 or 10000000, so ROUND_HALF_UP rounds to 0, giving output of 0E+7.

    When you subtract BigDecimals, the difference takes the maximum of the scales of the numbers being subtracted. So, the difference's scale is 0. The result is calculated to be 1, and the scale of 0 doesn't force a rounding here. The output is 1.

    To get the result 1 without having to subtract 1, you can subtract 0, or you can call setScale(0) on oneEPlus8.

    oneEPlus8 = oneEPlus8.setScale(0);
    System.out.println(oneEPlus8.divide(oneHundredMillion, BigDecimal.ROUND_HALF_UP));
    

    This outputs:

    1
    

    As an aside, you can use divide(BigDecimal, RoundingMode) to divide specifying a RoundingMode enum instead of the older int constants.