java浮点数精度问题不是bug,而是ieee 754二进制存储机制的必然结果;0.1在二进制中无限循环,double用52位尾数截断导致误差,故0.1+0.2=0.30000000000000004。

Java浮点数精度问题不是bug,而是IEEE 754二进制存储机制的必然结果。0.1在二进制中是无限循环小数,double用52位尾数截断后必然带误差,所以0.1 + 0.2输出0.30000000000000004——这不是计算错,是存储就错了。
浮点数底层存储:为什么0.1存不准
double类型按IEEE 754标准占64位:1位符号 + 11位指数 + 52位尾数。十进制小数转二进制时,像0.1、0.2、0.3这类数无法有限表达,只能近似。例如:
- 0.1 → 二进制为
0.0001100110011...(无限循环) - 系统取前52位尾数并舍入,得到的是一个“最接近”的近似值,而非精确值
- 这个误差在加减乘除中会累积,比如循环累加10次0.1,结果是
0.9999999999999999
BigDecimal正确用法:避开构造陷阱
要用BigDecimal保证精度,关键在初始化和运算两步:
Java JDK 25 来自 OpenJDK 官方归档,版本为 JDK 25,本条下载地址已指向官方 Windows x64 zip 安装包直链,适合调试旧项目或兼容旧版 Java 运行环境。
- ❌ 错误:用
new BigDecimal(0.1)——直接把double的误差带进来 - ✅ 正确:用
new BigDecimal("0.1")或BigDecimal.valueOf(0.1)(后者内部做了字符串转换) - 除法必须指定精度和舍入模式:
a.divide(b, 2, RoundingMode.HALF_UP),否则抛ArithmeticException - 比较必须用
compareTo(),不能用equals()或==(后者比较对象引用,equals()还比较scale)
替代方案与性能权衡
并非所有场景都适合BigDecimal,要根据业务特点选型:
- 金融类金额:优先用BigDecimal,但建议统一用“分”为单位存整型(
long),避免构造开销和scale管理 - 科学计算/图形渲染:double足够,关注相对误差而非绝对相等,比较时用
Math.abs(a - b) - 高频批量计算:若精度要求不高(如统计均值),可先用double聚合,最后再转BigDecimal格式化输出
- Android或资源受限环境:考虑使用
long模拟定点数,或引入轻量级库(如apfloat)
常见误操作与规避要点
很多精度问题其实源于编码习惯,而非技术能力:
- 日志里打印
System.out.println(doubleValue)看似正常,但底层仍是误差值,后续参与计算就会暴露 - 数据库字段用
DOUBLE存金额,哪怕Java层用了BigDecimal,入库时又丢精度 - 前端传参用JSON数字(如
{"price": 19.99}),Jackson默认反序列化为double,误差已产生 - 修复建议:接口接收金额统一用字符串,后端解析为BigDecimal;数据库字段用
DECIMAL类型
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










