Fundamentals 24 min read

Beware of BigDecimal Pitfalls: When Precision Errors Can Cost You

This article explains why using Java's BigDecimal for high‑precision calculations requires careful constructor selection, demonstrates the hidden errors of the double‑based constructor, shows how to format and compare values, and provides a utility class with safe arithmetic methods.

Java Architect Essentials
Java Architect Essentials
Java Architect Essentials
Beware of BigDecimal Pitfalls: When Precision Errors Can Cost You

Java's java.math.BigDecimal class is designed for exact arithmetic on numbers with more than 16 significant digits, which the primitive double type cannot reliably represent. When developers need precise results—such as financial calculations or scientific measurements—they must avoid the imprecise double constructor and instead use the String constructor or BigDecimal.valueOf(double).

Common Constructors

BigDecimal(int) – creates a value from an integer.

BigDecimal(double) – creates a value from a double; the resulting value may contain unexpected binary‑to‑decimal conversion errors.

BigDecimal(long) – creates a value from a long.

BigDecimal(String) – creates a value exactly as the string represents, guaranteeing the expected decimal.

Problem Demonstration

BigDecimal a = new BigDecimal(0.1);
System.out.println("a values is:" + a);
BigDecimal b = new BigDecimal("0.1");
System.out.println("b values is:" + b);

Output:

a values is:0.1000000000000000055511151231257827021181583404541015625
=====================
b values is:0.1

Reason analysis:

The BigDecimal(double) constructor receives the binary representation of 0.1, which cannot be expressed exactly in a finite binary fraction, leading to a long, non‑terminating decimal expansion.

The BigDecimal(String) constructor parses the literal string "0.1" and stores the exact decimal value, making it predictable.

If a double must be the source, use BigDecimal.valueOf(double) because it internally applies Double.toString(double) before constructing the BigDecimal, preserving the expected value.

Common Methods

add(BigDecimal)

– addition subtract(BigDecimal) – subtraction multiply(BigDecimal) – multiplication divide(BigDecimal) – division (may throw ArithmeticException if the result is non‑terminating) toString(), doubleValue(), floatValue(), longValue(), intValue() – conversions

Comparing Values

Use compareTo to obtain -1, 0, or 1 for less‑than, equal, or greater‑than respectively:

int a = bigDecimal1.compareTo(bigDecimal2); // -1, 0, 1

Formatting with NumberFormat

Because NumberFormat.format() accepts a BigDecimal, you can format currency or percentages without losing precision:

NumberFormat currency = NumberFormat.getCurrencyInstance();
NumberFormat percent = NumberFormat.getPercentInstance();
percent.setMaximumFractionDigits(3);
BigDecimal loan = new BigDecimal("15000.48");
BigDecimal rate = new BigDecimal("0.008");
BigDecimal interest = loan.multiply(rate);
System.out.println("贷款金额:\t" + currency.format(loan));
System.out.println("利率:\t" + percent.format(rate));
System.out.println("利息:\t" + currency.format(interest));

Result:

贷款金额: ¥15,000.48 利率: 0.8% 利息: ¥120.00

Handling Division Exceptions

When dividing non‑terminating decimals, BigDecimal.divide() throws

java.lang.ArithmeticException: Non‑terminating decimal expansion

. The fix is to specify a scale and rounding mode:

BigDecimal result = dividend.divide(divisor, 2, BigDecimal.ROUND_HALF_UP);

Utility Class Example

package com.vivo.ars.util;
import java.math.BigDecimal;
/**
 * High‑precision arithmetic utilities.
 */
public class ArithmeticUtils {
    private static final int DEF_DIV_SCALE = 10;
    public static double add(double v1, double v2) {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.add(b2).doubleValue();
    }
    public static BigDecimal add(String v1, String v2) {
        return new BigDecimal(v1).add(new BigDecimal(v2));
    }
    // Similar methods for sub, mul, div, round, remainder, compare, etc.
}

Summary

Use BigDecimal only when exact decimal arithmetic is required; for routine calculations, double or float is faster. Prefer the String constructor (or valueOf) to avoid hidden precision errors, remember that BigDecimal is immutable, and always store the result of an operation. The provided utility class demonstrates a clean, reusable API for common arithmetic tasks.

JavaPerformancePrecisionArithmeticBigDecimalNumberFormatUtility
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

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.