Why BigDecimal Can Lose Precision and How to Safely Handle Money in Java

This article examines a real‑world cash‑register failure caused by BigDecimal precision loss, explains why using double or float constructors leads to errors, demonstrates the problem with code samples, and provides a robust utility class for accurate monetary calculations in Java.

Java Backend Technology
Java Backend Technology
Java Backend Technology
Why BigDecimal Can Lose Precision and How to Safely Handle Money in Java

Background

When calculating or displaying monetary amounts we often use BigDecimal, which is highly recommended for financial values. However, misuse of its many constructors can cause unnecessary trouble or even monetary loss, leading to incidents.

Incident

The cash register reported an error that prevented order payment, causing a drop in payment success rate to 60%.

Accident Level

P0

Incident Process

13:44 – Alarm received, order payment failed.

13:50 – Quickly rolled back the deployed code and restored normal operation.

14:20 – Reviewed the code and identified the problem in pre‑release testing.

14:58 – Fixed the problematic code and redeployed, restoring service.

Root Cause

The loss of precision occurs when BigDecimal is constructed from double or float values. The underlying doubleToRawLongBits method converts the floating‑point number to a long using native (C++) code, and binary representation of decimal fractions inevitably loses precision.

Reason Analysis

Floating‑point types ( float and double) are designed for scientific and engineering calculations, providing fast approximations over a wide range but not exact results. When the fractional part is converted to binary, it may produce an infinite repeating sequence or exceed the mantissa length, leading to rounding errors. BigDecimal avoids this by scaling the decimal number to an integer and retaining the scale information.

Summary

For precise monetary calculations, always construct BigDecimal from a String (or other exact representations) rather than from floating‑point literals.

Correct Usage

BigDecimal bigDecimal2 = new BigDecimal("8.8");
BigDecimal bigDecimal3 = new BigDecimal("8.812");
System.out.println(bigDecimal2.compareTo(bigDecimal3));
System.out.println(bigDecimal2.add(bigDecimal3));

Note that BigDecimal objects must be operated on using their own methods (add, subtract, multiply, divide); they cannot be used with the traditional arithmetic operators.

Tool Sharing

The following utility class provides convenient methods for precise arithmetic with double and

float> values by internally converting them to <code>BigDecimal

using String constructors.

import java.math.BigDecimal;

public class BigDecimalUtils {
    public static BigDecimal doubleAdd(double v1, double v2) {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.add(b2);
    }
    public static BigDecimal floatAdd(float v1, float v2) {
        BigDecimal b1 = new BigDecimal(Float.toString(v1));
        BigDecimal b2 = new BigDecimal(Float.toString(v2));
        return b1.add(b2);
    }
    public static BigDecimal doubleSub(double v1, double v2) {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.subtract(b2);
    }
    public static BigDecimal floatSub(float v1, float v2) {
        BigDecimal b1 = new BigDecimal(Float.toString(v1));
        BigDecimal b2 = new BigDecimal(Float.toString(v2));
        return b1.subtract(b2);
    }
    public static BigDecimal doubleMul(double v1, double v2) {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.multiply(b2);
    }
    public static BigDecimal floatMul(float v1, float v2) {
        BigDecimal b1 = new BigDecimal(Float.toString(v1));
        BigDecimal b2 = new BigDecimal(Float.toString(v2));
        return b1.multiply(b2);
    }
    public static BigDecimal doubleDiv(double v1, double v2) {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.divide(b2, 2, BigDecimal.ROUND_HALF_UP);
    }
    public static BigDecimal floatDiv(float v1, float v2) {
        BigDecimal b1 = new BigDecimal(Float.toString(v1));
        BigDecimal b2 = new BigDecimal(Float.toString(v2));
        return b1.divide(b2, 2, BigDecimal.ROUND_HALF_UP);
    }
    public static int doubleCompareTo(double v1, double v2) {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.compareTo(b2);
    }
    public static int floatCompareTo(float v1, float v2) {
        BigDecimal b1 = new BigDecimal(Float.toString(v1));
        BigDecimal b2 = new BigDecimal(Float.toString(v2));
        return b1.compareTo(b2);
    }
}

Illustrations

BigDecimal precision demonstration
BigDecimal precision demonstration
Binary representation of decimal fractions
Binary representation of decimal fractions
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaBigDecimalutilityMoney
Java Backend Technology
Written by

Java Backend Technology

Focus on Java-related technologies: SSM, Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading. Occasionally cover DevOps tools like Jenkins, Nexus, Docker, and ELK. Also share technical insights from time to time, committed to Java full-stack development!

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.