文章标题:
浮点数运算之迷:0.1与0.2相加非0.3?BigDecimal正确应用探索
文章内容:
目录
一、起始:为何简单加法会出现偏差?
(一)10%加20%等于0.3?计算器为何不按常理出牌?
0.1加0.2不等于0.3的反常规现象
(二)从“顺理成章”到“惊讶”的转变
二、误差根源:浮点数精度困境
(一)为何0.1无法被精确表示?
类比:十进制中1/3无法精确表示
(二)Java里double是如何存储的?(IEEE 754简介)
举个例子:double d = 0.1;
(三)几个典型的反直觉计算实例
✅ 实例1:加法误差
✅ 实例2:金额加法
✅ 实例3:金额减法
(四)小误差,大问题:一分钱损失如何演变成30万?
📌 背景情况
📌 存在问题
📌 吸取教训
小结:你以为的“0.1”,计算机里可能是“0.10000000000000001”
三、第一道防护:BigDecimal的正确使用
(一)BigDecimal真能解决所有问题吗?
(二)new BigDecimal(double)的重大隐患
📌 问题展示
📌 内在机制
📌 推荐做法
✅ 正确性对比
(三)推荐构造方式及差异
(四)scale和precision的影响及不良影响(通过实际乘法案例分析)
✅ 基本概念
📌 真实案例:乘法未指定舍入模式时抛出异常
📌 另一种错误用法:精度丢失
(五)小结:BigDecimal很强大,但要用对方法
四、第二道防护:浮点数的格式化与舍入
(一)String.format为何得出怪异的3.4和3.3?
📌 原因剖析
📌 银行家舍入模式解读(HALF_EVEN)
(二)RoundingMode的各类模式与默认行为
(三)DecimalFormat与BigDecimal:谁更可靠?
✅ DecimalFormat:适用于展示层(String)
✅ BigDecimal:适用于运算和持久化
✅ 实战建议
推荐格式化方式:BigDecimal + setScale + RoundingMode
(四)小结:展示不等同于计算,格式不等同于精度
五、判等陷阱:float/double/BigDecimal的“==”误解
(一)为何amount1 - amount2 == 1.05为假?
📌 原因解析
⚠️ 危险的误判后果
(二)equals()和compareTo()的区别
📌 为何equals不安全?
✅ 推荐:使用compareTo() == 0判等
(三)浮点判等的三种思路(场景实用指南)
✅ 思路一:绝对误差阈值(适用于double)
✅ 思路二:BigDecimal精确判等(适用于金额)
✅ 思路三:compareTo == 0判等
(四)实战踩坑实例:开发者A的误判之苦
(五)小结:判等,是浮点世界最后一道坑
六、总结与建议:给开发者的避坑指南
(一)🧠 浮点数开发四步骤:从思考到实施
1. 存储方式选对了吗?
2. 运算方式可靠吗?
3. 格式化环节安全吗?
4. 比较操作正确吗?
(二)✅ 最佳实践清单:金融系统浮点数安全用法
(三)📌 何时可以“不用太较真”?
(四)💡 技术背后的意识:你在构建一个“可信赖的系统”
(五)🎯 最后的金句总结(送给每位开发者)
📘 全文回顾
干货分享,感谢阅读!
“就加个0.1+0.2,怎么就不是0.3了?”
“同一个接口,线上调账1分,结果少了300,000元?”
听着像段子,但这类事故在金融系统、支付平台、清结算逻辑中并不少见。
在程序世界里,0.1+0.2≠0.3不是bug,而是数学与计算机底层的差异冲突。浮点数的本质、Java中double的局限、BigDecimal的使用误区、格式化的陷阱、判等的误解……这些都可能让你写出看似完美的代码,却埋下金额出错的隐患。

这篇文章,将带你逐步揭开