Understanding Precision Loss in Java's BigDecimal and Proper Usage
The article explains why floating‑point arithmetic in Java loses precision, demonstrates how even BigDecimal can suffer when constructed from double values, and shows the correct practice of using the BigDecimal(String) constructor together with utility methods to perform accurate monetary calculations.
When monetary calculations are required, using float or double leads to precision loss, so developers are advised to use BigDecimal . The article starts by showing typical floating‑point errors with simple arithmetic.
System.out.println(0.05 + 0.01);
System.out.println(1.0 - 0.42);
System.out.println(4.015 * 100);
System.out.println(123.3 / 100);The output demonstrates values such as 0.060000000000000005 and 401.49999999999994 , illustrating why e‑commerce systems may encounter ordering or reconciliation problems.
Java's float provides about 6‑7 significant digits, while double offers 15‑16 digits, still insufficient for exact monetary representation.
API
Constructors:
BigDecimal(int) // creates from an int value
BigDecimal(double) // creates from a double value (may lose precision)
BigDecimal(long) // creates from a long value
BigDecimal(String) // creates from a string representation (recommended)Methods:
add(BigDecimal) // addition
subtract(BigDecimal) // subtraction
multiply(BigDecimal) // multiplication
divide(BigDecimal) // division
toString() // convert to string
doubleValue() // convert to double
floatValue() // convert to float
longValue() // convert to long
intValue() // convert to intEven BigDecimal can lose precision if instantiated with a double . The following example shows the problem:
BigDecimal a = new BigDecimal(1.01);
BigDecimal b = new BigDecimal(1.02);
BigDecimal c = new BigDecimal("1.01");
BigDecimal d = new BigDecimal("1.02");
System.out.println(a.add(b));
System.out.println(c.add(d));Output:
2.0300000000000000266453525910037569701671600341796875
2.03The double‑based constructor yields a long, inaccurate result, while the String constructor produces the exact value. This occurs because binary floating‑point cannot represent many decimal fractions precisely; the underlying hardware stores numbers in limited bits (19 digits for long , about 16 for double ).
Therefore, for commercial calculations one should always create BigDecimal objects with the BigDecimal(String) constructor, as recommended by Effective Java and MySQL guidelines.
The JDK source comment further explains that the BigDecimal(double) constructor is unpredictable and that converting the double to a string first yields an exact conversion:
* The results of this constructor can be somewhat unpredictable.
* One might assume that writing {@code new BigDecimal(0.1)} creates a BigDecimal exactly equal to 0.1,
* but it is actually equal to 0.1000000000000000055511151231257827021181583404541015625.
* When a double must be used as a source for a BigDecimal, use BigDecimal.valueOf(double) instead.Because BigDecimal is an object, arithmetic operators cannot be used directly; one must call the appropriate methods, and the method arguments must also be BigDecimal instances.
To simplify usage, the article provides a utility class:
/**
* @author Ji YongGuang.
* @date 19:50 2017/12/14.
*/
public class BigDecimalUtil {
private BigDecimalUtil() {}
public static BigDecimal add(double v1, double v2) {
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.add(b2);
}
public static BigDecimal sub(double v1, double v2) {
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.subtract(b2);
}
public static BigDecimal mul(double v1, double v2) {
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.multiply(b2);
}
public static BigDecimal div(double v1, double v2) {
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
// retain two decimal places, round half up
return b1.divide(b2, 2, BigDecimal.ROUND_HALF_UP);
}
}This utility offers static methods for addition, subtraction, multiplication, and division of double values while preserving precision by converting them to BigDecimal via Double.toString .
Selected Java Interview Questions
A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!
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.