Fundamentals 7 min read

Understanding Java's BigDecimal: How It Guarantees Precision

This article explains why Java's BigDecimal class maintains exact precision by examining its internal fields, demonstrating usage through test code, and walking through the add method's implementation to show how values are scaled and computed as long integers before producing the final result.

Java Architect Essentials
Java Architect Essentials
Java Architect Essentials
Understanding Java's BigDecimal: How It Guarantees Precision

In financial applications, Java developers often rely on BigDecimal to avoid precision loss. This article explores why BigDecimal can guarantee exact decimal arithmetic.

Class Overview

The core fields of BigDecimal are shown below:

public class BigDecimal extends Number implements Comparable
{
    // 该BigDecimal的未缩放值
    private final BigInteger intVal;
    // 精度,可以理解成小数点后的位数
    private final int scale;
    // BigDecimal中的十进制位数,如果位数未知,则为0(备用信息)
    private transient int precision;
    // Used to store the canonical string representation, if computed.
    // 这个我理解就是存实际的BigDecimal值
    private transient String stringCache;
    // 扩大成long型数值后的值
    private final transient long intCompact;
}

Example Usage

A simple JUnit test demonstrates basic operations:

@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);
}

Running the test shows the internal fields being populated after calling BigDecimal.valueOf(2.36) .

add Method Implementation

The public add method delegates to several overloaded private methods based on whether the operands are stored as compact long values or as BigInteger objects:

/**
 * 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 core arithmetic occurs in the private static method that works with long representations:

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);
        }
    }
}

When both operands fit into a long , the method simply adds them and returns 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(ys), scale);
}

Conclusion

The analysis shows that BigDecimal achieves loss‑less precision by scaling decimal numbers into integer representations (typically long ), performing integer arithmetic, and then applying the stored scale to produce the final decimal result. The article also points readers to further documentation for best practices.

javaProgrammingprecisionArithmeticBigDecimal
Java Architect Essentials
Written by

Java Architect Essentials

Committed to sharing quality articles and tutorials to help Java programmers progress from junior to mid-level to senior architect. We curate high-quality learning resources, interview questions, videos, and projects from across the internet to help you systematically improve your Java architecture skills. Follow and reply '1024' to get Java programming resources. Learn together, grow together.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.