Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java's Bigdecimal.divide and rounding

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.

like image 625
Raphael do Vale Avatar asked Oct 19 '14 16:10

Raphael do Vale


People also ask

How do you split BigDecimal?

divide(BigDecimal divisor)scale() – divisor. scale()). Parameters: This method accepts a parameter divisor by which this BigDecimal is to be divided for obtaining quotient. Return value: This method returns a BigDecimal which holds the result (this / divisor).

What is scale in BigDecimal divide?

A BigDecimal consists of an arbitrary precision integer unscaled value and a 32-bit integer scale.

What is rounding mode in BigDecimal Java?

Rounding mode is to round toward the nearest neighbor, unless both neighbors are equidistant (and in this case, round up). This is the default. UNNECESSARY. Rounding mode is to assert that the requested operation has an exact result, and therefore no rounding is necessary.

Does BigDecimal support decimal places?

BigDecimal represents an immutable arbitrary-precision signed decimal number. It consists of two parts: Unscaled value – an arbitrary precision integer. Scale – a 32-bit integer representing the number of digits to the right of the decimal point.


3 Answers

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.

like image 143
dcernahoschi Avatar answered Oct 22 '22 13:10

dcernahoschi


The expressions new BigDecimal("176100000") and new BigDecimal("1761e+5") are not equal. BigDecimal keeps track of both value, and precision.

BigDecimal("176100000") has 9 digits of precision and is represented internally as the BigInteger("176100000"), multiplied by 1. BigDecimal("1761e+5") has 4 digits of precision and is represented internally as the BigInteger("1761"), multiplied by 100000.

When you a divide a BigDecimal by a value, the result respects the digits of precision, resulting in different outputs for seemingly equal values.

like image 7
Joshua Avatar answered Oct 22 '22 12:10

Joshua


for your division with BigDecimal.

dividendo.divide(divisor,2,RoundingMode.CEILING)//00.00 nothing for up and nothing for down

in this operation have a precision for two decimals.

like image 5
leandro lion Avatar answered Oct 22 '22 12:10

leandro lion