Skip to content
Preview · sample content · Explore all pages

Java BigDecimal: decimal amounts and explicit rounding

Last updated: 28 Sept 20263 min read
tutorial
IntermediateBy AITrove Editorial

BigDecimal represents an arbitrary-precision decimal value using an unscaled integer and a scale, with immutable arithmetic results.

Start from a decimal representation

For a defined decimal amount, construct from the input text or another exact decimal source. Passing a double to the constructor captures that binary floating-point value, which may differ from the decimal text the developer intended.

The program calculates tax from a textual subtotal and rate, rounds tax once to two places, then adds it to the subtotal. These are demonstration values. A real invoice needs a defined currency, tax policy, rounding stage, and maximum amount.

Immutable means add, multiply, and setScale return new values. Calling total.add(tax) without keeping its result does not update total. This differs from a mutable balance object with an increment method.

Equality includes scale

equals distinguishes 2.0 and 2.00 because it compares both numeric value and scale. compareTo regards them as numerically equal. Pick the comparison that matches the application rule.

This affects map keys and set membership. Normalising scale at a boundary can establish one representation, but an arbitrary normalisation may destroy meaningful formatting or precision rules.

Division can require a rounding decision when the exact decimal expansion does not terminate. Use an explicit scale and RoundingMode or a MathContext suitable for the operation; silently assuming every decimal division is exact will fail.

Working program

Java
import java.math.BigDecimal;
import java.math.RoundingMode;

public class InvoiceTaxCalculation {
    public static void main(String[] args) {
        BigDecimal subtotal = new BigDecimal("19.95");
        BigDecimal taxRate = new BigDecimal("0.18");
        BigDecimal tax = subtotal.multiply(taxRate).setScale(2, RoundingMode.HALF_UP);
        BigDecimal total = subtotal.add(tax);
        System.out.println("tax=" + tax.toPlainString());
        System.out.println("total=" + total.toPlainString());
        System.out.println(new BigDecimal("2.0").equals(new BigDecimal("2.00")));
        System.out.println(new BigDecimal("2.0").compareTo(new BigDecimal("2.00")) == 0);
    }
}

Output

Output
tax=3.59
total=23.54
false
true

Cost and design choices

Arbitrary precision is not constant-cost arithmetic. Larger digit counts require more computation and storage; multiplication and division costs depend on operand sizes and the algorithms in use.

Limit accepted precision and scale at an input boundary. An attacker-supplied amount with an enormous digit count can create computation and memory pressure even before a business rule rejects its value.

Converting a final decimal amount back to double can reintroduce binary rounding. Keep the decimal representation through persistence and interchange when the format supports it.

Common Mistakes

  • Do not construct an exact monetary input from an imprecise double by accident.
  • Do not ignore a returned arithmetic value.
  • Do not substitute equals for numeric comparison without considering scale.
  • Do not leave rounding policy implicit.
java
bigdecimal
Storage details