Fundamentals 6 min read

Why BigDecimal Can Lose Precision: A Real-World Checkout Failure Explained

This article analyzes a checkout‑system outage caused by BigDecimal precision loss, reproduces the bug with Java code, explains the underlying double‑to‑long conversion issue, and recommends using String‑based construction for accurate monetary calculations.

Programmer DD
Programmer DD
Programmer DD
Why BigDecimal Can Lose Precision: A Real-World Checkout Failure Explained

Background

We often use BigDecimal when calculating or displaying monetary amounts, and it is highly recommended for financial values.

However, improper use of its constructors can cause unnecessary trouble or even monetary loss, leading to incidents.

Incident

The checkout service experienced an error while calculating product prices, preventing orders from being paid.

Problem Description

Checkout failed to compute the amount, causing payment failure.

Incident Level

P0

Incident Timeline

13:44 – Alert received, payment success rate dropped to 60%.

13:50 – Quickly rolled back the deployed code, service restored.

14:20 – Reviewed code; pre‑release testing identified the issue.

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

Root Cause

Precision loss in BigDecimal when handling monetary calculations.

Analysis

First, we reproduce the problem with a short Java program:

public static void main(String[] args) {
    BigDecimal bigDecimal = new BigDecimal(88);
    System.out.println(bigDecimal);
    bigDecimal = new BigDecimal("8.8");
    System.out.println(bigDecimal);
    bigDecimal = new BigDecimal(8.8);
    System.out.println(bigDecimal);
}

Running the program yields the following output:

Testing shows that using double or float loses precision, while String and int do not. To understand why, we inspect the source of the constructor:

public static long doubleToLongBits(double value) {
    long result = doubleToRawLongBits(value);
    // Check for NaN based on values of bit fields, maximum
    // exponent and nonzero significand.
    if (((result & DoubleConsts.EXP_BIT_MASK) == DoubleConsts.EXP_BIT_MASK) &&
        (result & DoubleConsts.SIGNIF_BIT_MASK) != 0L)
        result = 0x7ff8000000000000L;
    return result;
}

The problem lies in doubleToRawLongBits, which converts a double to a long using native (C++) code. The loss occurs because converting a decimal fraction to binary cannot represent the exact value.

BigDecimal

handles this by scaling the decimal number to an integer and keeping the precision information.

Key Points

1. float and double are designed for scientific and engineering calculations, providing fast approximations over a wide range.

2. They do not guarantee exact results and should not be used where precision is required.

3. Large floating‑point numbers are automatically expressed in scientific notation, which is an approximation.

4. Converting decimal fractions to binary can produce infinite repeats or exceed the mantissa length.

Conclusion

Therefore, when precise calculations are needed, especially for money, prefer constructing BigDecimal from String (or integer) values rather than from floating‑point literals.

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.

JavaprecisionBigDecimalfloating-pointFinancial-Calculations
Programmer DD
Written by

Programmer DD

A tinkering programmer and author of "Spring Cloud Microservices in Action"

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.