Java浮点数精度丢失是IEEE 754二进制表示固有局限所致,0.1+0.2=0.30000000000000004属必然现象;金融等场景应优先用BigDecimal(字符串构造、方法运算、指定舍入)或整数放大法;科学计算可用误差阈值比较;格式化仅用于显示,不修复精度。

Java 中浮点数精度丢失不是 bug,而是 IEEE 754 二进制表示的固有局限:像 0.1、0.2、0.66 这类十进制小数,在二进制中是无限循环小数(如 0.1 → 0.0001100110011…),而 float(23 位尾数)和 double(52 位尾数)只能截断存储,导致从源头就存在舍入误差。后续加减乘除会累积放大,所以 0.1 + 0.2 输出 0.30000000000000004 是必然现象。
优先用 BigDecimal 做精确计算
这是金融、计费、订单等“1分钱都不能错”场景的唯一可靠方案:
- 构造必须用字符串:
new BigDecimal("0.1")✅;禁用new BigDecimal(0.1)❌(此时传入的 0.1 已失真) - 所有运算调用方法:
add()、subtract()、multiply()、divide() - 除法必须指定精度和舍入模式:
a.divide(b, 2, RoundingMode.HALF_UP)(保留 2 位,四舍五入) - 比较用
compareTo(),不用==或equals()(后者会比 scale)
整数放大法替代浮点运算
适合性能敏感、精度要求明确(如金额单位为“分”、温度保留 1 位小数)的场景:
分析 Java Spring Boot 后端 Controller 代码,自动生成 Markdown 格式的接口文档。当用户说“生成 Java 接口文档”、“生成 API 文档”、“从 Spring 代码生成文档”时触发。
- 将数值按倍数转为 long/int:例如价格
19.99 元 → 1999 分,全程整数加减 - 注意安全放大:用
Math.round(value * scale)避免因截断导致的向下取整错误 - 风险点:放大倍数过大可能溢出(如
long超过9223372036854775807),需预估数据范围
浮点数比较改用误差阈值
适用于科学计算、物理模拟等允许微小误差的场景:
- 不写
if (a == b),改写为if (Math.abs(a - b) - 阈值选择参考:普通计算用
1e-10,图形渲染可用1e-6,物理引擎常用1e-8 - 该方式仅解决“判断是否相等”,不修复底层存储误差,不可用于后续精确计算
显示格式化不等于精度修复
很多人误以为用 String.format("%.2f", d) 或 DecimalFormat 就能“修好精度”,其实不然:
- 这些只是对已失真的 double 值做四舍五入后截断显示,底层值仍是
0.30000000000000004 - 若该值再参与下一次计算(如累加、比较),误差仍会暴露
- 真正需要精确结果时,格式化只能放在最后一步(如输出日志、前端展示),绝不能替代 BigDecimal 或整数运算
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










