Backend Development 7 min read

Understanding Java BigDecimal: Class Structure, Example, and Precision Mechanism

This article explains why Java's BigDecimal can guarantee lossless precision in financial calculations by examining its class definition, internal fields, example usage, and the detailed implementation of its add method, including scaling and long‑integer arithmetic.

Top Architecture Tech Stack
Top Architecture Tech Stack
Top Architecture Tech Stack
Understanding Java BigDecimal: Class Structure, Example, and Precision Mechanism

In the financial domain, precise decimal arithmetic is essential, so developers often use Java's BigDecimal . This article explores why BigDecimal can guarantee that precision is never lost.

Class Introduction

The declaration of BigDecimal and its key fields are shown below:

public class BigDecimal extends Number implements Comparable<BigDecimal> {
    // the unscaled value of this BigDecimal
    private final BigInteger intVal;
    // scale, i.e., number of digits after the decimal point
    private final int scale;
    // number of decimal digits (unused, defaults to 0)
    private transient int precision;
    // cached canonical string representation, if computed
    private transient String stringCache;
    // compact long representation of the value
    private final transient long intCompact;
}

Example Test

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

After calling BigDecimal.valueOf(2.36) , debugging shows the fields above being populated.

How add Works

The public add method delegates to an internal static method that handles scaling:

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

When the test runs, the parameters passed to the private static add are xs = 236 , scale1 = 2 , ys = 35 , scale2 = 1 . The method first computes the difference between the scales (here 1) and follows the branch that scales the smaller‑scale operand:

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 this case, sdiff is positive, so the method scales ys (the operand with the smaller scale) by multiplying it by 10 1 , turning 35 into 350, then adds the two long values (236 + 350 = 586) and finally creates a new BigDecimal with scale 2, yielding 5.86 .

The final addition step is performed by a simple helper:

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

Therefore, BigDecimal maintains precision by converting decimal numbers into scaled long integers, performing integer arithmetic (which is exact), and then applying the stored scale to produce the final decimal representation.

When using BigDecimal , be aware of its API nuances; further details can be found at the referenced documentation:

https://javaguide.cn/java/basis/bigdecimal.html
Javabackend developmentprecisionBigDecimalNumber Handling
Top Architecture Tech Stack
Written by

Top Architecture Tech Stack

Sharing Java and Python tech insights, with occasional practical development tool tips.

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.