Java浮点数误差源于IEEE 754二进制限制,应改用BigDecimal(字符串构造、指定舍入)或整数运算(如“分”代替“元”);浮点数仅用于非精确场景,比较须用误差范围而非==。

Java 中浮点型(float、double)本身无法避免计算误差——这是由 IEEE 754 二进制表示机制决定的底层限制,不是代码写法问题。真正可行的路径是:**不依赖 float/double 做精确计算,改用更适合的类型或策略**。
优先用 BigDecimal 处理十进制精度敏感场景
适用于金额、税率、账务对账等必须“所见即所得”的业务:
- 永远用
new BigDecimal("0.1")这类字符串构造,绝不用new BigDecimal(0.1)(后者会把 double 的误差直接带进来) - 除法必须指定小数位数和舍入模式,例如:
a.divide(b, 2, RoundingMode.HALF_UP) - 比较两个值用
a.compareTo(b) == 0,别用equals()(后者会比较 scale,0.1 和 0.10 不相等) - 加减乘可直接调用
add()/subtract()/multiply(),结果天然精确
用整数代替小数做运算
尤其适合货币类场景,把“元”转成“分”处理:
- 价格 19.99 元 → 存为
long priceInCents = 1999L - 所有计算都在整数层面进行,完全规避浮点误差
- 展示时再除以 100,并按需格式化(如用
String.format("%.2f", priceInCents / 100.0))
浮点数仅用于非精确场景,且谨慎比较
若确实需要 float/double(如图形渲染、传感器数据、科学模拟),请接受其近似本质:
- 绝不写
a == b,改用差值判断:Math.abs(a - b) - 避免累加大量小浮点数,考虑使用 Kahan 求和等补偿算法(极少数高要求场景)
- 大数与小数混合运算时警惕精度吞噬(如
1e16 + 1 == 1e16)
别踩这些常见坑
这些操作看似合理,实则悄悄引入误差:
- 用
Double.toString(0.1)再构造 BigDecimal?不必要——BigDecimal.valueOf(0.1)已内部做了这步,更简洁安全 - 用
setScale()而不指定舍入模式?默认是UNNECESSARY,遇到需舍入就抛异常 - 在数据库里用
DOUBLE或FLOAT存金额?应改用DECIMAL(p,s)类型
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











