Sitemap
Rambabu Penanda

Rambabu Penanda

Backend Engineer & Technical Lead | Distributed Systems | Java, Spring Boot, Kafka, Redis | System Design & Scalable Architecture

Money Is Not Just a Number

3 min readSep 27, 2026

--

Why decimal precision matters in Java for FinTech, payments, and user wallet(Gaming) systems

Press enter or click to view image in full size
When the number represents money, precision is part of correctness.

Introduction

0.1 + 0.2 = 0.30000000000000004

In a coding interview, this is a classic Java question. In a wallet, payment, or FinTech system, it becomes a correctness problem.

Money is not just another number. The way we represent, calculate, round, store, and update monetary values directly affects balances, payments, settlements, refunds, and reconciliation.

1. Why double Is Not Suitable for Money

Java’s double uses binary floating-point representation. Many decimal fractions, including 0.1 and 0.2, cannot be represented exactly in binary.

double a = 0.1; 
double b = 0.2;
System.out.println(a + b); // 0.30000000000000004

The important point is that this is not random inaccuracy. The decimal values are being represented approximately in a binary floating-point format.

2. Why new BigDecimal(double) Can Still Be Wrong

BigDecimal x = new BigDecimal(0.1); 
BigDecimal y = new BigDecimal(0.2);
System.out.println(x.add(y)); // 0.300000000000000016653345…

The problem is that 0.1 and 0.2 have already become double values before BigDecimal receives them. new BigDecimal(double) preserves the exact value of that binary floating-point number.

Get Rambabu Penanda’s stories in your inbox

Join Medium for free to get updates from this writer.

Key Idea: BigDecimal is precise, but it cannot recover the decimal value you intended if you first give it an imprecise double.

3. The Correct Way

Use an exact decimal string:

BigDecimal x = new BigDecimal("0.1"); 
BigDecimal y = new BigDecimal("0.2");
System.out.println(x.add(y)); // 0.3

Or use BigDecimal.valueOf() for decimal literals:

BigDecimal x = BigDecimal.valueOf(0.1); 
BigDecimal y = BigDecimal.valueOf(0.2);
System.out.println(x.add(y)); // 0.3

Best Practice: For monetary calculations, prefer new BigDecimal(String) or BigDecimal.valueOf(...) over new BigDecimal(double).

4. Rounding and Scale Are Business Rules

BigDecimal amount = new BigDecimal("10.555");
BigDecimal rounded = amount.setScale(2, RoundingMode.HALF_UP);
System.out.println(rounded); // 10.56

Precision alone is not enough. Financial systems need explicit rules for scale and rounding. The correct rounding mode depends on the business requirement and the type of financial calculation.

5. Wallet Balance Updates Need More Than BigDecimal

Suppose two requests try to debit the same wallet at the same time. Even if both requests use BigDecimal correctly, a race condition can still produce an incorrect balance.

UPDATE wallet 
SET balance = balance - :amount
WHERE user_id = :userId
AND balance >= :amount;

The update should be designed as an atomic operation where appropriate, with the database transaction boundary and concurrency behavior clearly defined.

6. Idempotency Matters in Money Movement

Payment and wallet requests can be retried because of timeouts or network failures. The same logical operation must not create a duplicate financial effect.

POST /wallet/debit
Idempotency-Key: txn-123456

A unique transaction or idempotency key lets the system recognize retries and prevent duplicate debits or credits.

7. Balance vs. Ledger

A wallet balance represents a current state. A ledger records the financial events that produced that state.

  • Wallet: user_id, balance
  • Ledger: transaction_id, user_id, type, amount, currency, status, created_at

Maintaining an auditable transaction history makes reconciliation, debugging, support, and financial investigations much easier.

8. Database Representation

CREATE TABLE wallet ( 
user_id BIGINT PRIMARY KEY,
balance NUMERIC(19, 2) NOT NULL
);

NUMERIC/DECIMAL is commonly used when exact decimal storage is required. The appropriate precision and scale should be based on the application’s currency and business requirements.

9. BigDecimal Equality: equals() vs compareTo()

new BigDecimal("1.0").equals(new BigDecimal("1.00")); // false
new BigDecimal("1.0").compareTo(new BigDecimal("1.00")); // 0

equals() considers scale, while compareTo() compares numerical value. This distinction matters deeply in financial validations and tests.

10. Practical FinTech Checklist

  • Do not use double/float for exact monetary values.
  • Prefer new BigDecimal(String) or BigDecimal.valueOf().
  • Define scale and rounding rules explicitly.
  • Keep currency as part of the domain model.
  • Use NUMERIC/DECIMAL or an appropriate minor-unit representation.
  • Make wallet mutations atomic.
  • Use idempotency for retriable money operations.
  • Maintain an auditable ledger or transaction history.
  • Handle concurrency and race conditions explicitly.
  • Design reconciliation and failure handling from the beginning.

--

--

Rambabu Penanda
Rambabu Penanda

Backend Engineer & Technical Lead | Distributed Systems | Java, Spring Boot, Kafka, Redis | System Design & Scalable Architecture