Understanding Java BigDecimal: How It Guarantees Decimal Precision
This article explains how Java's BigDecimal class maintains exact decimal precision by scaling numbers to integers, detailing its class structure, key methods, and a step‑by‑step example that demonstrates the internal addition algorithm.
In financial applications, precise decimal calculations are crucial, so developers often use Java's BigDecimal . This article explores why BigDecimal can preserve precision without loss.
Class Overview
The BigDecimal class extends Number and implements Comparable<BigDecimal> . Its main fields include the unscaled value ( intVal ), the scale (number of digits after the decimal point), a cached string representation, and other internal helpers.
public class BigDecimal extends Number implements Comparable
{
// unscaled value
private final BigInteger intVal;
// scale (number of decimal places)
private final int scale;
private transient int precision;
private transient String stringCache;
private final transient long intCompact;
}Example Usage
A simple JUnit test demonstrates creating two BigDecimal instances and adding them:
@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);
}When BigDecimal.valueOf(2.36) is executed, the debugger shows the internal fields being populated with the unscaled value 236 and scale 2 .
How add Works
The public add method delegates to a private static overload that handles different internal representations. The key part is the method that receives two long values and their scales:
private static BigDecimal add(final long xs, int scale1, final long ys, int scale2) {
long sdiff = (long) scale1 - scale2;
if (sdiff == 0) {
return add(xs, ys, scale1);
} else if (sdiff < 0) {
int raise = checkScale(xs, -sdiff);
long scaledX = longMultiplyPowerTen(xs, raise);
if (scaledX != INFLATED) {
return add(scaledX, ys, scale2);
} else {
BigInteger bigsum = bigMultiplyPowerTen(xs, raise).add(ys);
return ((xs ^ ys) >= 0) ? new BigDecimal(bigsum, INFLATED, scale2, 0)
: valueOf(bigsum, scale2, 0);
}
} else {
int raise = checkScale(ys, sdiff);
long scaledY = longMultiplyPowerTen(ys, raise);
if (scaledY != INFLATED) {
return add(xs, scaledY, scale1);
} else {
BigInteger bigsum = bigMultiplyPowerTen(ys, raise).add(xs);
return ((xs ^ ys) >= 0) ? new BigDecimal(bigsum, INFLATED, scale1, 0)
: valueOf(bigsum, scale1, 0);
}
}
}In the example, the method receives xs = 236 , scale1 = 2 , ys = 35 , and scale2 = 1 . The scale difference is 1 , so the algorithm scales the second operand (35) by ten to 350 before performing integer addition.
After scaling, the private add method for two long values simply adds them and constructs a new BigDecimal with the appropriate scale:
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(BigInteger.valueOf(ys)), scale);
}Conclusion
Therefore, BigDecimal maintains precision by converting decimal numbers into scaled long (or BigInteger ) representations, performing integer arithmetic, and then applying the original scale to produce the final result without any loss of decimal accuracy.
Architecture Digest
Focusing on Java backend development, covering application architecture from top-tier internet companies (high availability, high performance, high stability), big data, machine learning, Java architecture, and other popular fields.
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.