ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

浮点数的陷阱:你以为的 0.1 + 0.2其实不是 0.3,BigDecimal你真的用对了吗?

浮点数的陷阱:你以为的 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 vs BigDecimal:谁才靠谱?✅ DecimalFormat:适合展示层(String)✅ BigDecimal:适合运算和持久化✅ 实战建议推荐格式化方式:BigDecimal + setScale + RoundingMode(四)小结:展示不等于计算,格式不等于精度五、判等陷阱:float/double/BigDecimal 的“==”误会(一)为什么 amount1 - amount2 == 1.05 为 false?📌 原因解析⚠️ 危险的误判后果(二)equals() 和 compareTo() 的区别📌 为什么 equals 不安全?✅ 推荐:使用 compareTo() == 0 判等(三)浮点判等的三种思路(场景实用指南)✅ 思路一:绝对误差阈值(适用于 double)✅ 思路二:BigDecimal 精确判等(适用于金额)✅ 思路三:compareTo == 0 判等(四)实战踩坑示例:开发者 A 的误判之痛(五)小结:判等,是浮点世界最后一道坑六、总结与建议:写给开发者的避坑手册(一)🧠 浮点数开发四步法:从思考到落地1. 存储方式选对了吗?2. 运算方式可靠了吗?3. 格式化环节安全了吗?4. 比较操作正确了吗?(二)✅ 最佳实践 Checklist:金融系统浮点数安全用法(三)📌 什么时候可以“不那么较真”?(四)💡 技术背后的意识:你是在建一个“可信赖的系统”(五)🎯 最后的金句总结(送给每一位开发者)📘 全文回顾干货分享,感谢您的阅读!“就加了个 0.1 + 0.2,怎么就不是 0.3 了?”“同一个接口,线上调账 1 分,结果整整少了 300,000 元?”听上去像是段子,但这类事故在金融系统、支付平台、清结算逻辑中并不罕见。在程序世界里,0.1 + 0.2 != 0.3并不是一个 bug,而是数学与计算机底层的差异冲突。浮点数的本质、Java 中 double 的局限、BigDecimal 的使用误区、格式化的陷阱、判等的误解……这一切都可能让你写下看似完美的代码,却埋下金额出错的隐雷。这篇文章,将带你逐步揭开:为什么看起来“理所当然”的小数运算,在计算机中会变得“不讲理”?Java 中到底该怎么写,才能让金额计算、比较、输出都安全可靠?
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进