Java BigDecimal 正确用法,四大核心坑避坑教程

做 Java 后端开发,只要涉及金额、税费、利息这类高精度计算,所有人都会告诉你:别用 double,用 BigDecimal。
但很多人只知道“要用 BigDecimal”,却不知道 BigDecimal 本身藏着一堆致命坑。用错了,照样会出现精度丢失、计算错误、线上故障,甚至给公司造成直接经济损失。
今天就把 BigDecimal 最常见、最致命的四大坑一次性讲透,每一个都是真实业务里踩出来的血泪教训,看完直接避坑,再也不翻车。
一、浮点精度的坑:别让 double 毁了你的 BigDecimal
这是 BigDecimal 最经典、最容易踩的坑,没有之一。
很多人图省事,直接用 double 类型来构造 BigDecimal:
// 错误写法! BigDecimal num = new BigDecimal(0.1); System.out.println(num);
你以为输出的是 0.1?
实际输出:0.1000000000000000055511151231257827021181583404541015625
坑在哪里?
double 本身就是二进制浮点类型,天生就无法精确表示 0.1 这类十进制小数。当你把 0.1 这个不精确的 double 值传给 BigDecimal 时,BigDecimal 会忠实地保留这个不精确的精度,相当于从源头就把精度丢了。
正确写法:
永远用字符串来初始化 BigDecimal,从根源保证精度:
// 正确写法
BigDecimal num = new BigDecimal("0.1");
System.out.println(num); // 输出:0.1
或者用BigDecimal.valueOf(double)方法,它内部会先把 double 转成字符串再构造,避免精度丢失:
// 安全写法 BigDecimal num = BigDecimal.valueOf(0.1); System.out.println(num); // 输出:0.1
二、设置精度的坑:除法不指定舍入模式,直接崩
做金额计算,除法是绕不开的。但很多人写除法时,直接这么写:
// 错误写法!
BigDecimal a = new BigDecimal("1");
BigDecimal b = new BigDecimal("3");
System.out.println(a.divide(b));
运行直接报错:
java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
坑在哪里?
BigDecimal 的除法默认要求绝对精确。当除不尽时(比如 1/3),会产生无限循环小数,BigDecimal 无法精确表示,就会直接抛出算术异常,导致程序崩溃。
正确写法:
除法必须指定保留的小数位数和舍入模式,尤其是金额计算,必须明确舍入规则:
// 正确写法:保留 2 位小数,四舍五入 BigDecimal result = a.divide(b, 2, RoundingMode.HALF_UP); System.out.println(result); // 输出:0.33
重点提醒:舍入模式别乱选
- RoundingMode.HALF_UP:四舍五入,金额计算 99%场景用这个
- RoundingMode.DOWN:直接截断,不进位
- RoundingMode.UP:向上进位
- 不同舍入模式会直接影响计算结果,用错了就是财务事故!
三、初始化的坑:equals()和 compareTo()的天差地别
很多人用 BigDecimal 做比较时,直接用 equals()方法:
BigDecimal a = new BigDecimal("0.1");
BigDecimal b = new BigDecimal("0.10");
System.out.println(a.equals(b));
你以为输出 true?
实际输出:false
坑在哪里?
BigDecimal 的equals()方法,不仅比较数值,还会比较标度(scale)。0.1 的标度是 1,0.10 的标度是 2,所以 equals()认为它们不相等。
而我们做金额比较时,只关心数值是否相等,不关心小数点后有几个 0。
正确写法:
永远用 compareTo()方法做数值比较:
// 正确写法:0 表示相等,1 表示 a>b,-1 表示 a<b System.out.println(a.compareTo(b) == 0); // 输出:true
补充:a.compareTo(b) == 0等价于数值相等,>0 表示 a 大于 b,<0 表示 a 小于 b。
四、转字符串的坑:toString()和 toPlainString()的区别
很多人用 BigDecimal 转字符串时,直接用 toString():
BigDecimal num = new BigDecimal("0.0000001");
System.out.println(num.toString());
你以为输出 0.0000001?
实际输出:1E-7(科学计数法)
坑在哪里?
当数值过大或过小时,toString()会自动用科学计数法表示。如果直接把这个字符串存到数据库、传给前端,就会出现格式错误,导致数据异常。
正确写法:
永远用 toPlainString()方法,它会返回普通的十进制字符串,不会用科学计数法:
// 正确写法 System.out.println(num.toPlainString()); // 输出:0.0000001
反之,对于很大的数,比如 1000000000000,toString()会输出 1E+12,toPlainString()会输出完整的 1000000000000,完全满足业务需求。
五、总结:BigDecimal 避坑黄金法则
把这四大坑总结成 5 条黄金法则,照着写,永远不翻车:
- 初始化:永远用字符串,别用 double
禁止new BigDecimal(0.1),用new BigDecimal("0.1")或BigDecimal.valueOf(0.1) - 除法:必须指定精度和舍入模式
禁止a.divide(b),必须写a.divide(b, 2, RoundingMode.HALF_UP) - 比较:用
compareTo(),别用 equals()
禁止a.equals(b),用a.compareTo(b) == 0判断相等 - 转字符串:用
toPlainString(),别用 toString()
禁止num.toString(),用num.toPlainString()避免科学计数法 - 运算:优先用方法引用,避免手动计算
用add()、subtract()、multiply()、divide(),别自己手动转换
BigDecimal 本身是为了解决精度问题而生的,但用错了,反而会带来更多问题。尤其是金额计算,一个小数点的错误,就是真金白银的损失。
以上关于Java BigDecimal 正确用法,四大核心坑避坑教程的文章就介绍到这了,更多相关内容请搜索码云笔记以前的文章或继续浏览下面的相关文章,希望大家以后多多支持码云笔记。
如若内容造成侵权/违法违规/事实不符,请将相关资料发送至 admin@mybj123.com 进行投诉反馈,一经查实,立即处理!
重要:如软件存在付费、会员、充值等,均属软件开发者或所属公司行为,与本站无关,网友需自行判断
码云笔记 » Java BigDecimal 正确用法,四大核心坑避坑教程
微信
支付宝