Understanding Why BigDecimal Preserves Precision in Java
This article explains how Java's BigDecimal class maintains exact decimal precision by scaling numbers to long integers, detailing its internal fields, demonstrating addition with sample code, and analyzing the underlying add method logic to show why no precision is lost.
In financial applications, precise decimal calculations are crucial, so Java developers often use BigDecimal . This article explores why BigDecimal can guarantee loss‑less precision.
Class Overview
Below is the declaration of the BigDecimal class and its key fields:
public class BigDecimal extends Number implements Comparable
{
// the unscaled value of this BigDecimal
private final BigInteger intVal;
// scale (number of digits after the decimal point)
private final int scale;
// cached precision (unused in this example)
private transient int precision;
// cached string representation
private transient String stringCache;
// compact long representation when possible
private final transient long intCompact;
}Example Walkthrough
Debugging the source code helps reveal the class behavior. Consider the following test method:
@Test
public void testBigDecimal() {
BigDecimal bigDecimal1 = BigDecimal.valueOf(2.36);
BigDecimal bigDecimal2 = BigDecimal.valueOf(3.5);
BigDecimal resDecimal = bigDecimal1.add(bigDecimal2);
System.out.println(resDecimal);
}After calling BigDecimal.valueOf(2.36) , the debugger shows the fields described above being populated.
Next, we dive into the add method to see how the calculation is performed:
/**
* Returns a BigDecimal whose value is (this + augend),
* and whose scale is max(this.scale(), augend.scale()).
*/
public BigDecimal add(BigDecimal augend) {
if (this.intCompact != INFLATED) {
if (augend.intCompact != INFLATED) {
return add(this.intCompact, this.scale, augend.intCompact, augend.scale);
} else {
return add(this.intCompact, this.scale, augend.intVal, augend.scale);
}
} else {
if (augend.intCompact != INFLATED) {
return add(augend.intCompact, augend.scale, this.intVal, this.scale);
} else {
return add(this.intVal, this.scale, augend.intVal, augend.scale);
}
}
}The parameters passed to the private static add overload are:
xs = 236
scale1 = 2
ys = 35
scale2 = 1
The method first computes scale1 - scale2 (which equals 1) and follows the branch where the scales differ. It raises the smaller‑scale operand by a factor of 10 ( n = raise = 1 ), turning ys into scaledY = 350 . Then it adds the two long values:
private static BigDecimal add(long xs, long ys, int scale) {
long sum = add(xs, ys);
if (sum != INFLATED)
return BigDecimal.valueOf(sum, scale);
return new BigDecimal(BigInteger.valueOf(xs).add(ys), scale);
}This simple routine computes the sum as a long and constructs a new BigDecimal with the appropriate scale, ensuring that the arithmetic is performed on integer values, which inherently preserves precision.
Conclusion
Therefore, BigDecimal maintains exact precision by scaling decimal numbers to long (or BigInteger ) representations, performing integer arithmetic, and then applying the stored scale to produce the final result.
Code Ape Tech Column
Former Ant Group P8 engineer, pure technologist, sharing full‑stack Java, job interview and career advice through a column. Site: java-family.cn
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.