Mastering Java BigDecimal: Precise Calculations, Constructors, and Common Pitfalls
This article provides a comprehensive guide to Java's BigDecimal class, covering its purpose for high‑precision arithmetic, common constructors, usage pitfalls with double values, essential methods, comparison techniques, formatting options, exception handling, and a utility class for everyday operations.
BigDecimal Overview
Java provides the java.math.BigDecimal class for precise arithmetic on numbers with more than 16 significant digits. While double can handle up to 16 digits, many applications require higher precision or smaller numbers, making BigDecimal essential.
BigDecimal Common Constructors
Common Constructors
BigDecimal(int)– creates an object with the specified integer value. BigDecimal(double) – creates an object with the specified double value (may introduce precision issues). BigDecimal(long) – creates an object with the specified long value. BigDecimal(String) – creates an object with the value represented by the given string, preserving exact precision.
Usage Issues 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 BigDecimal(double) constructor can produce unexpected values because many decimal fractions (e.g., 0.1) cannot be represented exactly in binary floating‑point.
The BigDecimal(String) constructor is deterministic and yields the exact expected value.
When a double must be used as the source, prefer BigDecimal.valueOf(double), which internally uses Double.toString(double) to avoid the precision loss.
Common BigDecimal Methods
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 (may throw an exception if the division is non‑terminating). 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.
BigDecimal Size Comparison
Use the compareTo method to compare two BigDecimal instances:
int a = bigDecimal1.compareTo(bigDecimal2);Result interpretation: a = -1 – first value is smaller. a = 0 – values are equal. a = 1 – first value is larger.
BigDecimal Formatting
The NumberFormat class can format BigDecimal values for currency, percentages, or general numbers.
Example:
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("贷款金额:\t" + currency.format(loanAmount));
System.out.println("利率:\t" + percent.format(interestRate));
System.out.println("利息:\t" + currency.format(interest));Result:
贷款金额: ¥15,000.48 利率: 0.8% 利息: ¥120.00Formatting with two decimal places (padding with zeros when necessary):
public class NumberFormat {
public static void main(String[] s) {
System.out.println(formatToNumber(new BigDecimal("3.435")));
System.out.println(formatToNumber(new BigDecimal(0)));
System.out.println(formatToNumber(new BigDecimal("0.00")));
System.out.println(formatToNumber(new BigDecimal("0.001")));
System.out.println(formatToNumber(new BigDecimal("0.006")));
System.out.println(formatToNumber(new BigDecimal("0.206")));
}
/**
* @desc 1.0~1之间的BigDecimal小数,格式化后失去前面的0,则前面直接加上0。
* 2.传入的参数等于0,则直接返回字符串"0.00"
* 3.大于1的小数,直接格式化返回字符串
*/
public static String formatToNumber(BigDecimal obj) {
DecimalFormat df = new DecimalFormat("#.00");
if (obj.compareTo(BigDecimal.ZERO) == 0) {
return "0.00";
} else if (obj.compareTo(BigDecimal.ZERO) > 0 && obj.compareTo(new BigDecimal(1)) < 0) {
return "0" + df.format(obj).toString();
} else {
return df.format(obj).toString();
}
}
}Result:
3.44
0.00
0.00
0.00
0.01
0.21Common Exceptions
Division Exception
java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal resultCause Analysis
When BigDecimal.divide is used on a non‑terminating decimal without specifying a scale, the operation throws the above exception.
Solution
Specify a scale in the divide method, e.g., divide(divisor, 2) , to define the number of decimal places and avoid the exception.
Summary
Summary
Use BigDecimal only when exact decimal computation is required; its performance is lower than double and float, especially for large or complex calculations. Prefer the String constructor to avoid precision loss. BigDecimal objects are immutable; each arithmetic operation creates a new instance, so always store the returned result.
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;
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();
}
public static BigDecimal add(String v1, String v2) {
BigDecimal b1 = new BigDecimal(v1);
BigDecimal b2 = new BigDecimal(v2);
return b1.add(b2);
}
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();
}
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();
}
public static BigDecimal sub(String v1, String v2) {
BigDecimal b1 = new BigDecimal(v1);
BigDecimal b2 = new BigDecimal(v2);
return b1.subtract(b2);
}
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();
}
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();
}
public static BigDecimal mul(String v1, String v2) {
BigDecimal b1 = new BigDecimal(v1);
BigDecimal b2 = new BigDecimal(v2);
return b1.multiply(b2);
}
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);
}
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();
}
public static double div(double v1, double v2) {
return div(v1, v2, DEF_DIV_SCALE);
}
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();
}
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();
}
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();
}
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();
}
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();
}
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);
}
public static boolean compare(String v1, String v2) {
BigDecimal b1 = new BigDecimal(v1);
BigDecimal b2 = new BigDecimal(v2);
return b1.compareTo(b2) > 0;
}
}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 High-Performance Architecture
Sharing Java development articles and resources, including SSM architecture and the Spring ecosystem (Spring Boot, Spring Cloud, MyBatis, Dubbo, Docker), Zookeeper, Redis, architecture design, microservices, message queues, Git, etc.
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.
