BigDecimal 是 Java 中用于精确表示和计算十进制数的类,它专门为需要高精度计算的财务、金融等场景设计。float 和 double 是二进制浮点数,不适合用于精确的十进制计算,因为它们在表示某些小数时会产生舍入误差。

为什么需要 BigDecimal?
我们来看一个经典的例子,说明 float 和 double 的问题:
public class DoubleProblem {
public static void main(String[] args) {
double a = 0.1;
double b = 0.2;
System.out.println(a + b); // 预期输出 0.3,但实际输出 0.30000000000000004
}
}
这个结果就是二进制浮点数精度问题的体现,对于金额、利率等场景,这种微小的误差是绝对不能接受的。
BigDecimal 通过将数字表示为一个“未缩放的整数”和一个“小数点偏移量”(scale)来解决这个精度问题。
- 值 =
unscaledValue× 10^(-scale) 456可以表示为unscaledValue=123456,scale=3。
BigDecimal 的核心操作
创建 BigDecimal 对象
错误且常见的方式:

// 错误!0.1这个double值本身就不精确,然后传给BigDecimal BigDecimal bd1 = new BigDecimal(0.1); System.out.println(bd1); // 输出可能是 0.1000000000000000055511151231257827021181583404541015625
正确的方式:使用 String 构造方法
这是最推荐、最精确的方式,因为它直接解析十进制字符串。
BigDecimal bdFromString = new BigDecimal("0.1");
System.out.println(bdFromString); // 输出 0.1
其他推荐方式:使用 valueOf
BigDecimal.valueOf() 是一个静态工厂方法,它内部会先将 double 转换为 String,然后再创建 BigDecimal 对象,这比直接使用 double 构造要精确得多。

BigDecimal bdFromValueOf = BigDecimal.valueOf(0.1); System.out.println(bdFromValueOf); // 输出 0.1
- 首选
BigDecimal.valueOf(double)或new BigDecimal(String)。 - 绝对避免
new BigDecimal(double)。
四则运算
BigDecimal 的所有运算都不会改变原始对象,而是返回一个新的 BigDecimal 对象,你需要用这个新对象来接收结果。
BigDecimal a = new BigDecimal("10.5");
BigDecimal b = new BigDecimal("4.2");
// 加法
BigDecimal sum = a.add(b); // 14.7
// 减法
BigDecimal difference = a.subtract(b); // 6.3
// 乘法
BigDecimal product = a.multiply(b); // 44.1
// 除法 (最复杂,见下一节)
除法 - 精度控制
BigDecimal 的除法是精度管理的核心,由于两个数相除可能得到无限循环小数(如 10/3),你必须指定如何处理这种情况。
方法签名:
BigDecimal divide(BigDecimal divisor, int scale, RoundingMode roundingMode)
divisor: 除数scale: 小数点后保留的位数roundingMode: 舍入模式
常见的舍入模式:
RoundingMode.HALF_UP: “四舍五入”(最常用)。RoundingMode.UP: 远离零方向舍入(正数向上,负数向下)。RoundingMode.DOWN: 向零方向舍入(截断)。RoundingMode.CEILING: 向正无穷方向舍入。RoundingMode.FLOOR: 向负无穷方向舍入。
示例:
BigDecimal dividend = new BigDecimal("10");
BigDecimal divisor = new BigDecimal("3");
// 保留2位小数,四舍五入
BigDecimal result1 = dividend.divide(divisor, 2, RoundingMode.HALF_UP);
System.out.println(result1); // 输出 3.33
// 保留4位小数,四舍五入
BigDecimal result2 = dividend.divide(divisor, 4, RoundingMode.HALF_UP);
System.out.println(result2); // 输出 3.3333
// 保留0位小数,向上舍入
BigDecimal result3 = dividend.divide(divisor, 0, RoundingMode.UP);
System.out.println(result3); // 输出 4
异常情况:
如果除不尽,且你没有指定舍入模式,会抛出 ArithmeticException。
// 这行代码会抛出异常! // BigDecimal error = dividend.divide(divisor);
比较大小
不要使用 equals() 方法!
BigDecimal.equals() 不仅比较数值,还会比较 scale(小数位数),这在业务逻辑中通常不是我们想要的。
BigDecimal a = new BigDecimal("1.00");
BigDecimal b = new BigDecimal("1.0");
System.out.println(a.equals(b)); // 输出 false,因为 a.scale()=2, b.scale()=1
正确的方法:使用 compareTo()
compareTo() 只比较数值大小,返回 -1, 0, 或 1。
a.compareTo(b)< 0 => a < ba.compareTo(b)== 0 => a == ba.compareTo(b)> 0 => a > b
BigDecimal a = new BigDecimal("1.00");
BigDecimal b = new BigDecimal("1.0");
System.out.println(a.compareTo(b)); // 输出 0,表示数值相等
设置和获取精度
setScale(int newScale, RoundingMode roundingMode): 设置小数位数,并指定舍入模式。这会返回一个新的BigDecimal对象。setScale(int newScale): 设置小数位数,使用默认的舍入模式(RoundingMode.UNNECESSARY,表示不允许舍入,如果精度不够会抛出异常)。int scale(): 获取当前的小数位数。int precision(): 获取总的数字位数(不包括符号和小数点)。
BigDecimal price = new BigDecimal("123.4567");
// 设置为2位小数,四舍五入
BigDecimal roundedPrice = price.setScale(2, RoundingMode.HALF_UP);
System.out.println(roundedPrice); // 123.46
System.out.println(roundedPrice.scale()); // 2
System.out.println(roundedPrice.precision()); // 5 (1,2,3,4,6)
完整示例:购物车结算
这是一个综合运用 BigDecimal 的典型场景。
import java.math.BigDecimal;
import java.math.RoundingMode;
public class ShoppingCart {
public static void main(String[] args) {
// 商品单价
BigDecimal itemPrice1 = new BigDecimal("19.99");
BigDecimal itemPrice2 = new BigDecimal("5.99");
BigDecimal itemPrice3 = new BigDecimal("12.49");
// 商品数量
int quantity1 = 2;
int quantity2 = 1;
int quantity3 = 3;
// 计算小计 (价格 * 数量)
// 注意:不能直接用 int * BigDecimal,需要先将 int 转为 BigDecimal
BigDecimal subtotal1 = itemPrice1.multiply(BigDecimal.valueOf(quantity1));
BigDecimal subtotal2 = itemPrice2.multiply(BigDecimal.valueOf(quantity2));
BigDecimal subtotal3 = itemPrice3.multiply(BigDecimal.valueOf(quantity3));
// 计算总价
BigDecimal total = subtotal1.add(subtotal2).add(subtotal3);
// 假设折扣是 10%
BigDecimal discountRate = new BigDecimal("0.10");
BigDecimal discountAmount = total.multiply(discountRate);
// 计算折扣后价格,保留2位小数
BigDecimal discountedTotal = total.subtract(discountAmount).setScale(2, RoundingMode.HALF_UP);
// 假设税率是 8%
BigDecimal taxRate = new BigDecimal("0.08");
BigDecimal taxAmount = discountedTotal.multiply(taxRate).setScale(2, RoundingMode.HALF_UP);
// 计算最终应付金额
BigDecimal finalTotal = discountedTotal.add(taxAmount);
// 输出结果
System.out.println("商品1小计: $" + subtotal1);
System.out.println("商品2小计: $" + subtotal2);
System.out.println("商品3小计: $" + subtotal3);
System.out.println("商品总价: $" + total);
System.out.println("折扣金额: -$" + discountAmount.setScale(2, RoundingMode.HALF_UP));
System.out.println 