Master Precise Calculations in Java: A Deep Dive into BigDecimal
This article explains Java's BigDecimal class, covering its purpose, constructors, common methods, formatting techniques, handling of non‑terminating division, and best‑practice recommendations, while providing clear code examples and practical tips for accurate high‑precision arithmetic.
BigDecimal Overview
Java provides the BigDecimal class in the java.math package for precise arithmetic on numbers with more than 16 significant digits. While double can handle up to 16 digits, many applications require higher precision for very large or very small values.
For calculations that do not require exact precision, float and double are sufficient, but converting a String to double or float can lose precision. Therefore, when exact results are needed, BigDecimal must be used.
Because BigDecimal creates objects, arithmetic operators (+, -, *, /) cannot be used directly; instead, its methods must be called, and method parameters must also be BigDecimal instances. Constructors are special methods for creating objects, especially those with parameters.
Common Constructors
2.1 Common Constructors
BigDecimal(int)
BigDecimal(double)
BigDecimal(long)
BigDecimal(String)
These constructors create a BigDecimal object from the specified integer, double, long, or string value.
2.2 Usage Analysis
Example:
BigDecimal a = new BigDecimal(0.1);
System.out.println("a values is:" + a);
System.out.println("=====================");
BigDecimal b = new BigDecimal("0.1");
System.out.println("b values is:" + b);Result:
a values is:0.1000000000000000055511151231257827021181583404541015625
=====================
b values is:0.1Analysis:
The constructor that takes a double can produce an unpredictable result because the binary representation of 0.1 cannot be expressed exactly, leading to a long decimal expansion.
The String constructor is fully predictable; new BigDecimal("0.1") creates a value exactly equal to 0.1. Therefore, the String constructor is generally recommended.
If a double must be used as the source, use BigDecimal.valueOf(double), which internally converts the double to its string representation before creating the BigDecimal.
Common Methods Details
3.1 Common Methods
add(BigDecimal) – returns the sum of two BigDecimal values.
subtract(BigDecimal) – returns the difference.
multiply(BigDecimal) – returns the product.
divide(BigDecimal) – returns the quotient.
toString() – converts the value to a string.
doubleValue() – converts to a double .
floatValue() – converts to a float .
longValue() – converts to a long .
intValue() – converts to an int .
3.2 Size Comparison
In Java, BigDecimal comparison is usually performed with the compareTo method.
int result = bigDecimal1.compareTo(bigDecimal2);The result is: -1 if the first value is less than the second. 0 if they are equal. 1 if the first value is greater than the second.
Example usage:
if (new BigDecimal(a).compareTo(new BigDecimal(b)) >= 0) {
// a is greater than or equal to b
}BigDecimal Formatting
The NumberFormat class can format BigDecimal values for currency, percentages, or general numbers.
NumberFormat currency = NumberFormat.getCurrencyInstance();
NumberFormat percent = NumberFormat.getPercentInstance();
percent.setMaximumFractionDigits(3);
BigDecimal loanAmount = new BigDecimal("15000.48");
BigDecimal interestRate = new BigDecimal("0.008");
BigDecimal interest = loanAmount.multiply(interestRate);
System.out.println("Loan amount: " + currency.format(loanAmount));
System.out.println("Interest rate: " + percent.format(interestRate));
System.out.println("Interest: " + currency.format(interest));Result:
Loan amount: ¥15,000.48 Interest rate: 0.8% Interest: ¥120.00When formatting, BigDecimal values are rounded to two decimal places, padding with zeros if necessary.
Common Exceptions
5.1 Division Exception
java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal resultThis occurs when divide is called without specifying a scale for a non‑terminating decimal.
Solution: Use the overloaded divide method that specifies a scale, e.g., divide(divisor, 2) .
Summary
6.1 Summary
Use BigDecimal only when precise decimal calculations are required; it is slower than double and float, especially for large or complex operations. Prefer the String constructor to avoid precision loss. BigDecimal objects are immutable; each arithmetic operation creates a new instance, so the result must be stored.
6.2 Utility Class Recommendation
package com.vivo.ars.util;
import java.math.BigDecimal;
/**
* Utility class for high‑precision arithmetic operations.
*/
public class ArithmeticUtils {
private static final int DEF_DIV_SCALE = 10;
/** Precise addition for double values */
public static double add(double v1, double v2) {
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.add(b2).doubleValue();
}
/** Precise addition for String values */
public static BigDecimal add(String v1, String v2) {
BigDecimal b1 = new BigDecimal(v1);
BigDecimal b2 = new BigDecimal(v2);
return b1.add(b2);
}
/** Precise addition with scale for String values */
public static String add(String v1, String v2, int scale) {
if (scale < 0) throw new IllegalArgumentException("The scale must be a positive integer or zero");
BigDecimal b1 = new BigDecimal(v1);
BigDecimal b2 = new BigDecimal(v2);
return b1.add(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toString();
}
/** Precise subtraction for double values */
public static double sub(double v1, double v2) {
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.subtract(b2).doubleValue();
}
/** Precise subtraction for String values */
public static BigDecimal sub(String v1, String v2) {
BigDecimal b1 = new BigDecimal(v1);
BigDecimal b2 = new BigDecimal(v2);
return b1.subtract(b2);
}
/** Precise subtraction with scale for String values */
public static String sub(String v1, String v2, int scale) {
if (scale < 0) throw new IllegalArgumentException("The scale must be a positive integer or zero");
BigDecimal b1 = new BigDecimal(v1);
BigDecimal b2 = new BigDecimal(v2);
return b1.subtract(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toString();
}
/** Precise multiplication for double values */
public static double mul(double v1, double v2) {
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.multiply(b2).doubleValue();
}
/** Precise multiplication for String values */
public static BigDecimal mul(String v1, String v2) {
BigDecimal b1 = new BigDecimal(v1);
BigDecimal b2 = new BigDecimal(v2);
return b1.multiply(b2);
}
/** Precise multiplication with scale for double values */
public static double mul(double v1, double v2, int scale) {
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return round(b1.multiply(b2).doubleValue(), scale);
}
/** Precise multiplication with scale for String values */
public static String mul(String v1, String v2, int scale) {
if (scale < 0) throw new IllegalArgumentException("The scale must be a positive integer or zero");
BigDecimal b1 = new BigDecimal(v1);
BigDecimal b2 = new BigDecimal(v2);
return b1.multiply(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toString();
}
/** Precise division for double values */
public static double div(double v1, double v2) {
return div(v1, v2, DEF_DIV_SCALE);
}
/** Precise division with scale for double values */
public static double div(double v1, double v2, int scale) {
if (scale < 0) throw new IllegalArgumentException("The scale must be a positive integer or zero");
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).doubleValue();
}
/** Precise division with scale for String values */
public static String div(String v1, String v2, int scale) {
if (scale < 0) throw new IllegalArgumentException("The scale must be a positive integer or zero");
BigDecimal b1 = new BigDecimal(v1);
BigDecimal b2 = new BigDecimal(v2);
return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).toString();
}
/** Rounding to specified scale for double values */
public static double round(double v, int scale) {
if (scale < 0) throw new IllegalArgumentException("The scale must be a positive integer or zero");
BigDecimal b = new BigDecimal(Double.toString(v));
return b.setScale(scale, BigDecimal.ROUND_HALF_UP).doubleValue();
}
/** Rounding to specified scale for String values */
public static String round(String v, int scale) {
if (scale < 0) throw new IllegalArgumentException("The scale must be a positive integer or zero");
BigDecimal b = new BigDecimal(v);
return b.setScale(scale, BigDecimal.ROUND_HALF_UP).toString();
}
/** Remainder for String values with scale */
public static String remainder(String v1, String v2, int scale) {
if (scale < 0) throw new IllegalArgumentException("The scale must be a positive integer or zero");
BigDecimal b1 = new BigDecimal(v1);
BigDecimal b2 = new BigDecimal(v2);
return b1.remainder(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toString();
}
/** Remainder for BigDecimal values with scale */
public static BigDecimal remainder(BigDecimal v1, BigDecimal v2, int scale) {
if (scale < 0) throw new IllegalArgumentException("The scale must be a positive integer or zero");
return v1.remainder(v2).setScale(scale, BigDecimal.ROUND_HALF_UP);
}
/** Compare two String values */
public static boolean compare(String v1, String v2) {
BigDecimal b1 = new BigDecimal(v1);
BigDecimal b2 = new BigDecimal(v2);
return b1.compareTo(b2) > 0;
}
}This utility class provides static methods for precise addition, subtraction, multiplication, division, rounding, remainder calculation, and comparison using BigDecimal.
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.
Programmer DD
A tinkering programmer and author of "Spring Cloud Microservices in Action"
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.
