double for Currency🚨 Your Java Money Calculation Is Wrong — Stop Using double for Currency
This Java code looks harmless:
double price = 0.1;
double tax = 0.2;
System.out.println(price + tax);You might expect:
0.3But Java can print something like:
0.30000000000000004For scientific calculations, tiny floating-point differences may be acceptable.
For:
Money
Payments
Invoices
Tax
Interest
Accountingthey can become real bugs.
1. Why Does This Happen?
double uses binary floating-point representation.
Some decimal values cannot be represented exactly in binary.
Conceptually:
0.1
looks simple in decimal
but cannot be represented
exactly as a finite binary valueSo Java stores a nearby approximation.
Then calculations can expose the difference.
2. The Dangerous Payment Code
Imagine:
double price = 199.99;
double taxRate = 0.18;
double tax = price * taxRate;
double total = price + tax;Looks perfectly normal.
Now imagine doing similar calculations for:
1 transaction
100 transactions
1,000,000 transactionsRounding decisions start to matter.
Especially when totals must match:
Payment gateway
Invoice
Database
Accounting system
Refund calculationA one-cent/one-paise disagreement can become a reconciliation problem.
3. Never Compare Money Like This
Another common bug:
double expected = 0.3;
double actual = 0.1 + 0.2;
if (expected == actual) {
System.out.println("MATCH");
}The result may surprise you.
expected
0.3
actual
0.30000000000000004So:
expected == actualcan be:
falseNot what you want for financial business logic.
4. Use BigDecimal
For decimal financial calculations, Java provides:
BigDecimalExample:
BigDecimal price =
new BigDecimal("0.1");
BigDecimal tax =
new BigDecimal("0.2");
BigDecimal total =
price.add(tax);
System.out.println(total);Result:
0.3Much better for decimal money calculations.
5. But There’s Another Trap
Don’t do this:
BigDecimal amount =
new BigDecimal(0.1);You may carry the floating-point approximation into your BigDecimal.
Prefer:
new BigDecimal("0.1")or:
BigDecimal.valueOf(0.1)For fixed application values, string construction is very explicit.
6. Money Needs a Rounding Rule
Suppose:
BigDecimal amount =
new BigDecimal("100");
BigDecimal divisor =
new BigDecimal("3");This:
amount.divide(divisor);cannot produce a finite decimal result without a rounding decision.
So define one explicitly.
BigDecimal result =
amount.divide(
divisor,
2,
RoundingMode.HALF_UP
);Result:
33.33But the important point isn’t that HALF_UP is always correct.
The important point is:
Your business must define the rounding policy.
7. Different Businesses Need Different Rules
You might need:
HALF_UP
HALF_EVEN
DOWN
UPdepending on:
Currency rules
Tax rules
Banking rules
Accounting requirements
Business contractsDon’t scatter random rounding logic throughout the codebase.
Centralize it.
8. Be Careful With Scale
These values are numerically equal:
10.0
10.00But BigDecimal has both:
Value
+
ScaleThat creates an important Java trap.
BigDecimal a =
new BigDecimal("10.0");
BigDecimal b =
new BigDecimal("10.00");
System.out.println(a.equals(b));This returns:
falsebecause equals() considers scale.
Get Lakshika’s stories in your inbox
Join Medium for free to get updates from this writer.
But:
a.compareTo(b)returns:
0meaning the numerical values are equal.
9. Don’t Compare Money With equals() Blindly
Instead of:
if (amount.equals(expected)) {
...
}you may want numerical comparison:
if (amount.compareTo(expected) == 0) {
...
}depending on your domain requirements.
This tiny difference causes surprisingly confusing bugs.
10. Create a Clear Money Model
Instead of passing random BigDecimal values everywhere:
BigDecimal amountconsider modeling money explicitly.
For example:
public record Money(
BigDecimal amount,
Currency currency) {
}Now:
100 USDis different from:
100 INRbecause:
Amount alone ≠ MoneyCurrency matters too.
11. Store Money Carefully in the Database
Avoid:
price DOUBLEfor exact decimal financial values.
A common relational database choice is:
price DECIMAL(19, 4)or an appropriate precision and scale for your domain.
Then your application can map it to:
BigDecimalYour precision and scale should reflect actual business requirements.
12. Another Good Strategy: Minor Units
Some systems store money in the smallest currency unit.
Instead of:
₹199.99store:
19999 paiseusing an integer type.
Conceptually:
long amountInPaise = 19999;This can simplify some payment systems.
But remember:
Not every currency uses exactly two decimal places.
So don’t hardcode assumptions like:
Every currency = 100 minor unitsunless your system supports only currencies where that’s valid.
13. Example: Better Checkout Calculation
Instead of:
double subtotal = 999.99;
double tax =
subtotal * 0.18;
double total =
subtotal + tax;use:
BigDecimal subtotal =
new BigDecimal("999.99");
BigDecimal taxRate =
new BigDecimal("0.18");
BigDecimal tax =
subtotal
.multiply(taxRate)
.setScale(
2,
RoundingMode.HALF_UP
);
BigDecimal total =
subtotal.add(tax);Now the rounding decision is visible.
That’s important.
14. Before vs After
❌ Risky
double
0.1
+
0.2
↓
0.30000000000000004
↓
Rounding surprises
↓
Invoice mismatch✅ Better
BigDecimal
"0.1"
+
"0.2"
↓
0.3
↓
Explicit rounding
↓
Predictable financial logic15. Production Checklist
Search your financial code for:
✓ double price
✓ double amount
✓ double tax
✓ double balance
✓ double interest
✓ float moneyThen check:
✓ Are we using BigDecimal?
✓ Are values constructed safely?
✓ Is rounding explicit?
✓ Is rounding centralized?
✓ Is scale defined?
✓ Are BigDecimals compared correctly?
✓ Is currency stored with the amount?
✓ Does DB precision match Java?
✓ Are external payment amounts converted safely?
✓ Have boundary cases been tested?🔥 Final Takeaway
double isn't broken.
It’s simply designed for floating-point arithmetic where binary approximation is expected.
Financial systems often need something different:
Exact decimal representation
+
Explicit rounding
+
Clear currency rulesSo don’t write:
double money;just because money contains decimals.
Choose a representation that matches the business requirement.
One-Line Wisdom
Money isn’t just a number with decimals — it’s a value with precision, rounding rules, and a currency.











