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.
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).
A BigDecimal consists of an arbitrary precision integer unscaled value and a 32-bit integer scale.
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.
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.
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) anArithmeticException
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 expansionSince:
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With