BigDecimal Precision & Rounding Principles
Essential knowledge
Intended Audience:
Technical User
Authors:
Alexey Kaminskiy, Yulia Andreyanova
Changed on:
18 Feb 2026
Overview
This document defines the core principles for using`BigDecimal` in monetary and high-precision calculations. After reading, developers can apply the appropriate constructors, division rules, and rounding strategies to ensure consistent numeric representation across services, APIs, and storage. These principles reduce defects caused by floating-point arithmetic, undefined division, rounding errors, and incorrect value comparisons.Key points
- Monetary calculations rely on deterministic precision across all services.
- Precision is preserved using
`NUMERIC`(DB),`BigDecimal`(Java), and (`unscaledValue`,`scale`) (API). - Division requires explicit scale and rounding to avoid runtime exceptions.
- Most defects originate from constructing from
`double`, confusing scale vs precision, or misusing`equals()`.
These principles apply to all monetary and high-precision calculations, including cryptocurrency values (e.g., Ethereum with 18 decimal places). Their purpose is to ensure deterministic financial behavior, consistent numeric representation across system layers, lossless data transfer between DB, API, and business logic, and explicit, predictable rounding decisions.Precise Monetary Calculations Using
Floating-point types (Result: This is intentional behavior:
`BigDecimal` is immutable and represents decimal values as an unscaled integer combined with a scale. This model makes it suitable for financial and high-precision domains, but it also requires intentional management of scale and rounding to maintain consistency across services.In practice, most production issues originate from a small number of recurring patterns: constructing `BigDecimal` from `double` (binary artifacts), dividing without specifying rounding (runtime exceptions), confusing scale (decimal places) with precision (significant digits), and using `equals()` where `compareTo()` is required.The following sections describe the approved patterns and examples for working with `BigDecimal` to prevent inconsistent numeric representations across modules and integrations.Precise Monetary Calculations Using `BigDecimal`
Floating-point types (`double`, `float`) cannot precisely represent most decimal fractions due to binary encoding. They introduce hidden rounding errors and are not suitable for monetary values.For financial computations, the system consistently uses:
`NUMERIC`in the database`BigDecimal`in Java`(unscaledValue, scale)`representation in GraphQL
Example: Cryptocurrency Calculation (18 Decimals)
Order Creation
1{
2 "ref": "order-ref-ethereum",
3 ...
4 "preciseTotalPrice": {
5 "unscaledValue": 1234567,
6 "scale": 18
7 }
8}1BigDecimal preciseTotalPrice =
2 new BigDecimal(new BigInteger("1234567"), 18);`preciseAmount = 0.000000000001234567`Workflow Example (Capture & Shipping)
1//0.000000000001234567
2BigDecimal preciseTotalPrice =
3 order.getPreciseTotalPrice().getPreciseAmount()
4
5//0.000000000000000010
6BigDecimal deliveryPrice =
7 order.getFulfilmentChoice().getPreciseFulfilmentPrice().getPreciseAmount()
8
9BigDecimal captureAmount = preciseTotalPrice;
10
11//percentageToCapture = captureAmount / preciseTotalPrice
12//percentageToCapture = 0.000000000001234567 / 0.000000000001234567 = 1.000000000000000000
13BigDecimal percentageToCapture =
14 captureAmount.divide(preciseTotalPrice, 18, RoundingMode.HALF_UP);
15
16//shippingFee = deliveryPrice * percentageToCapture
17//shippingFee = 0.000000000000000010 * 1.000000000000000000 = 0.000000000000000010
18BigDecimal shippingFee =
19 deliveryPrice.multiply(percentageToCapture);
20
21//paymentTotal = captureAmount + shippingFee
22//paymentTotal = 0.000000000001234567 + 0.000000000000000010 = 0.000000000001234577
23BigDecimal paymentTotal =
24 captureAmount.add(shippingFee)
25 .setScale(18, RoundingMode.HALF_UP);1preciseTotalPrice = 0.000000000001234567
2percentageToCapture = 1.000000000000000000
3shippingFee = 0.000000000000000010
4paymentTotal = 0.000000000001234577Division Without Rounding
Unlike floating-point types,`BigDecimal` does not silently truncate repeating decimals.1// Throws ArithmeticException (non-terminating decimal expansion)
2new BigDecimal("1").divide(new BigDecimal("3"));
3
4// Correct: explicit scale + rounding mode
5new BigDecimal("1").divide(new BigDecimal("3"), 18, RoundingMode.HALF_UP);`BigDecimal` never performs implicit rounding, ensuring all financial rounding decisions remain explicit and consistent across services.`
`Constructor and Comparison Rules
1// ncorrect: constructing from double introduces precision artifacts
2BigDecimal incorrect = new BigDecimal(0.1);
3
4// ncorrect: equals() compares value AND scale
5new BigDecimal("1.0").equals(new BigDecimal("1.00")); // false
6
7// Correct: use String constructor for exact decimal value
8BigDecimal correct = new BigDecimal("0.1");
9
10// Correct: compareTo() compares numeric value only
11new BigDecimal("1.0").compareTo(new BigDecimal("1.00")) == 0; // true
12Rounding Strategies Overview
Different rounding modes support different business objectives. The chosen strategy affects:- customer-visible totals
- platform fees
- tax calculations
- long-term statistical bias
| Rounding Mode | Rule | Example (1.235 → scale 2) | Typical Use Case | Advantages | Risk / Trade-off |
| HALF_UP | Round to nearest; .5 rounds up | 1.24 | Retail pricing, payment totals, user-visible amounts | Intuitive, widely accepted, legally common | Small upward bias over many operations |
| HALF_EVEN | Round to nearest; .5 rounds to nearest even digit | 1.24 (1.245 → 1.24) | Banking systems, interest calculations, large-scale aggregation | Minimizes statistical rounding bias | Less intuitive for business users |
| DOWN | Always truncate extra digits (toward zero) | 1.23 | Fee calculation before remainder distribution, crypto minimal units | Never inflates value, deterministic, safe for splits | Systematic negative bias (always reduces value) |
| UP | Always round away from zero | 1.24 | Regulatory minimums, guaranteed fees | Ensures minimum thresholds | Systematic positive bias |
| CEILING | Round toward positive infinity | 1.24 | Tax calculations favoring authority | Predictable for positive-only domains | Biased upward |
| FLOOR | Round toward negative infinity | 1.23 | Conservative accounting rules | Predictable downward bias | Biased downward |
