Avoid the Top 5 BigDecimal Pitfalls in Java Money Calculations
This article explains five common BigDecimal traps—including constructor precision loss, equals vs compareTo, division exceptions, string conversion quirks, and operation order—provides code examples, and recommends using the JSR‑354 Money API with Maven for safer monetary calculations in Java backend development.
Five Common BigDecimal Pitfalls
1.1 new BigDecimal() vs BigDecimal.valueOf()
When constructing from a double, new BigDecimal(0.01) yields a long binary representation, while BigDecimal.valueOf(0.01) uses Double.toString and preserves the exact decimal value.
BigDecimal bd1 = new BigDecimal(0.01);
BigDecimal bd2 = BigDecimal.valueOf(0.01);
System.out.println("bd1 = " + bd1);
System.out.println("bd2 = " + bd2);The output shows
bd1 = 0.01000000000000000020816681711721685132943093776702880859375and bd2 = 0.01.
1.2 Equality vs Comparison
equalscompares both value and scale, so new BigDecimal("1.0").equals(new BigDecimal("1.00")) returns false, while compareTo returns 0 because the numeric values are equal.
BigDecimal bd1 = new BigDecimal("1.0");
BigDecimal bd2 = new BigDecimal("1.00");
System.out.println(bd1.equals(bd2)); // false
System.out.println(bd1.compareTo(bd2)); // 01.3 Division May Throw ArithmeticException
Dividing 1.0 by 3.0 without a rounding mode throws an exception because the result has a non‑terminating decimal expansion.
BigDecimal a = new BigDecimal("1.0");
BigDecimal b = new BigDecimal("3.0");
a.divide(b); // throws ArithmeticExceptionSpecify scale and rounding mode to obtain a finite result, e.g., a.divide(b, 2, RoundingMode.HALF_UP) yields 0.33.
1.4 Converting to String
BigDecimal.toString()may use scientific notation; use toPlainString() to avoid it, or toEngineeringString() for engineering format.
BigDecimal d = BigDecimal.valueOf(12334535345456700.12345634534534578901);
System.out.println(d.toString()); // 1.23345353454567E+16
System.out.println(d.toPlainString()); // 12334535345456700.123456345345345789011.5 Order of Operations
Multiplication is not always commutative with BigDecimal due to rounding. The following example shows different results when division is performed before multiplication.
BigDecimal a = BigDecimal.valueOf(1.0);
BigDecimal b = BigDecimal.valueOf(3.0);
BigDecimal c = BigDecimal.valueOf(3.0);
System.out.println(a.divide(b, 2, RoundingMode.HALF_UP).multiply(c)); // 0.99
System.out.println(a.multiply(c).divide(b, 2, RoundingMode.HALF_UP)); // 1.00Best Practices
Instead of using raw BigDecimal, wrap it with a money library such as JSR‑354 (Moneta). Add the Maven dependency:
<dependency>
<groupId>org.javamoney</groupId>
<artifactId>moneta</artifactId>
<version>1.1</version>
</dependency>Create a Money instance:
CurrencyUnit cny = Monetary.getCurrency("CNY");
Money money = Money.of(1.0, cny); // or Money.of(1.0, "CNY")Perform arithmetic safely:
Money oneYuan = Money.of(1.0, cny);
Money threeYuan = oneYuan.add(Money.of(2.0, "CNY")); // CNY 3
Money tenYuan = oneYuan.multiply(10); // CNY 10
Money fiveFen = oneYuan.divide(2); // CNY 0.5
System.out.println(fiveFen.equals(Money.of(0.50, "CNY"))); // trueThis approach adds explicit currency handling and avoids many BigDecimal pitfalls.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
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!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
