Java's Bigdecimal.divide and rounding

Raphael do Vale picture Raphael do Vale · Oct 19, 2014 · Viewed 28.9k times · Source

At work, we found a problem when trying to divide a large number by 1000. This number came from the database.

Say I have this method:

private static BigDecimal divideBy1000(BigDecimal dividendo) {
    if (dividendo == null) return null;

    return dividendo.divide(BigDecimal.valueOf(1000), RoundingMode.HALF_UP);
}

When I make the following call

divideBy1000(new BigDecimal("176100000"))

I receive the expected value of 176100. But if I try the line below

divideBy1000(new BigDecimal("1761e+5"))

I receive the value 200000. Why this occurs? Both numbers are the same with different representation and the latest is what I receive from database. I understand that, somehow, the JVM is dividing the number 1761 by 1000, rounding up and filling with 0's at the end.

What is the best way to avoid this kind of behavior? Keep in mind that the original number is not controlled by me.

Answer

dcernahoschi picture dcernahoschi · Oct 19, 2014

As specified in javadoc, a BigDecimal is defined by an integer value and a scale.

The value of the number represented by the BigDecimal is therefore (unscaledValue × 10^(-scale)).

So BigDecimal("1761e+5") has scale -5 and BigDecimal(176100000) has scale 0.

The division of the two BigDecimal is done using the -5 and 0 scales respectively because the scales are not specified when dividing. The divide documentation explains why the results are different.

divide

public BigDecimal divide(BigDecimal divisor)

Returns a BigDecimal whose value is (this / divisor), and whose preferred scale is (this.scale() - divisor.scale()); if the exact quotient cannot be represented (because it has a non-terminating decimal expansion) an ArithmeticException is thrown.

Parameters:

divisor - value by which this BigDecimal is to be divided.

Returns:

this / divisor

Throws:

ArithmeticException — if the exact quotient does not have a terminating decimal expansion

Since:

1.5

If you specify a scale when dividing, e.g. dividendo.divide(BigDecimal.valueOf(1000), 0, RoundingMode.HALF_UP) you will get the same result.