在 Java 开发中,float、double 浮点类型存在天然精度丢失问题,绝对无法用于金额、利息、汇率、精密计量等场景。Java 官方提供 BigDecimal 类,专门用于任意精度的十进制精确运算,是金融系统、支付项目、科学计算的核心必备类。
本文完整梳理 BigDecimal 的概述、核心优势、特性、对象创建、四则运算、舍入模式、比较规则、类型转换、高频踩坑点、实战规范,可直接用于开发落地与面试背诵。
一、BigDecimal 概述
BigDecimal 位于 java.math 包下,是 Java 专门用于高精度浮点数值运算的工具类。
主要应用场景:货币金额计算、金融利息结算、科学精密运算、工程测量、高精度统计等对数值精度要求极高的业务场景。
二、为什么需要 BigDecimal?(核心痛点)
float / double 采用 IEEE754 二进制浮点存储规范,无法精准表示所有十进制小数,会出现永久性精度丢失。
public class DoubleErrorTest {
public static void main(String[] args) {
System.out.println(0.1 + 0.2);
// 输出结果:0.30000000000000004
}
}
这种误差在金融场景是致命 Bug,而 BigDecimal 基于十进制字符串存储数值,从根源杜绝精度丢失。
三、BigDecimal 核心优势与特点
1. 任意精度存储
float/double 固定 32/64 位精度,而 BigDecimal 精度仅受限于当前设备内存,支持超大数、极小小数的超高精度运算。
2. 彻底避免精度丢失
不采用二进制近似存储,而是直接解析十进制数值,精准表示小数部分,无舍入误差。
3. 不可变类
BigDecimal 是不可变类,一旦对象创建完成,内部数值无法修改。
所有运算方法(加减乘除、保留精度)不会修改原对象,而是返回一个全新的 BigDecimal 对象。
💡 开发易错点:运算结果必须接收返回值,否则运算无效。
四、BigDecimal 对象创建(推荐与避坑)
BigDecimal 构造方式分推荐、不推荐两类,禁止使用 double 直接构造。
1. 推荐写法(无精度丢失)
// 方式1:字符串构造(最安全、企业首选)
BigDecimal bd1 = new BigDecimal("3.14159");
// 方式2:valueOf 静态方法(底层封装字符串,安全)
BigDecimal bd2 = BigDecimal.valueOf(0.1);
2. 不推荐写法(严重精度问题)
// 禁止!直接传入 double 会保留原生浮点误差
BigDecimal bd3 = new BigDecimal(0.1);
System.out.println(bd3);
// 输出:0.10000000000000000555...
原理:double 本身已经存在精度丢失,再封装为 BigDecimal 会将误差带入。
五、核心四则运算方法
所有运算必须接收返回值,原对象不改变。
1. 加法、减法、乘法、除法
BigDecimal a = new BigDecimal("10.5");
BigDecimal b = new BigDecimal("2.3");
// 加法
BigDecimal addRes = a.add(b);
// 减法
BigDecimal subRes = a.subtract(b);
// 乘法
BigDecimal mulRes = a.multiply(b);
// 除法(必须指定精度、舍入模式,必写!)
BigDecimal divRes = a.divide(b, 2, RoundingMode.HALF_UP);
2. 除法强制注意事项(高频报错)
BigDecimal 除法如果除不尽且不指定保留位数,直接抛出异常:ArithmeticException: Non-terminating decimal expansion。
所以业务中 divide 方法必须传两个参数:保留位数 + 舍入模式。
六、八大舍入模式详解
JDK1.5+ 推荐使用 RoundingMode 枚举(废弃旧的静态常量),核心常用 4 种:
1. RoundingMode.HALF_UP(四舍五入|最常用)
小数点后大于等于5进位,小于5舍去,金融、金额计算标准模式。
BigDecimal num1 = new BigDecimal("10");
BigDecimal num2 = new BigDecimal("3");
BigDecimal res = num1.divide(num2,2,RoundingMode.HALF_UP);
// 结果:3.33
2. RoundingMode.FLOOR(向下取整/向零舍入)
直接舍弃多余小数,不进位,数值向更小方向靠拢。
3. RoundingMode.CEILING(向上取整)
只要存在多余小数,直接进位,常用于费用向上取整场景。
4. RoundingMode.DOWN(直接截断)
无条件舍弃尾数,仅保留指定位数,无任何进位。
七、BigDecimal 等值比较(超级大坑)
1. 禁止使用 equals() 比较!
equals() 会同时比较数值 + 精度位数,会导致数值相等但精度不同时判定为不相等。
BigDecimal bd1 = new BigDecimal("10.5");
BigDecimal bd2 = new BigDecimal("10.500");
System.out.println(bd1.equals(bd2)); // false
2. 标准写法:使用 compareTo()(业务唯一推荐)
compareTo() 仅比较真实数值大小,忽略精度尾数 0。
- 返回 0:两个数值相等
- 返回 -1:当前值小于比较值
- 返回 1:当前值大于比较值
int result = bd1.compareTo(bd2);
System.out.println(result); // 0 代表相等
八、BigDecimal 与基本类型转换
转换为整数类型会直接截断小数部分,不会四舍五入,需手动处理精度。
BigDecimal bd = new BigDecimal("123.456");
// 转int(舍弃小数)
int intVal = bd.intValue(); // 123
// 转long
long longVal = bd.longValue(); // 123
// 转double(慎用,会丢失高精度)
double doubleVal = bd.doubleValue(); // 123.456
九、高频开发规范与避坑总结
- ✅ 创建对象优先使用
new BigDecimal("字符串")或BigDecimal.valueOf() - ❌ 禁止使用 double 构造 BigDecimal
- ✅ 所有运算必须接收返回值,不可原地修改
- ✅ 除法必须指定保留位数与舍入模式
- ✅ 等值比较只用
compareTo,禁用equals - ✅ 金额计算统一使用
HALF_UP四舍五入 - ✅ 数据库金额字段对应 decimal 类型,不使用 double
十、总结
BigDecimal 是 Java 高精度运算的唯一解决方案,彻底解决 float/double 的浮点精度丢失问题,是金融、支付、精密计算项目的必备核心类。