java中float和double不是算不准,而是因ieee 754标准用二进制表示十进制小数(如0.1、0.2)时必然产生截断误差,属确定性行为;float为32位(1位符号+8位指数+23位尾数),仅保证6~7位十进制精度,double为64位(1位符号+11位指数+52位尾数),精度更高但问题本质未变;所有依赖精确相等或零误差累积的场景(如金融计算)必须禁用浮点数,改用bigdecimal(字符串构造)或decimal类型。

Java 中的 float 和 double 不是“算不准”,而是用二进制科学计数法表示十进制小数时,天然存在表达局限——0.1、0.2 这类常见小数在二进制里是无限循环小数,存储时被截断,误差随之产生。这不是 bug,是 IEEE 754 标准下的确定性行为。
float 的 32 位怎么存一个数?
一个 float 占 4 字节(32 位),拆成三块:
- 符号位(1 位):0 是正,1 是负
- 指数位(8 位):带偏移量 127,实际指数范围约 −126 到 +127
- 尾数位(23 位):隐含前导 1,共 24 位有效精度,但只能保证 6~7 位十进制有效数字
例如:16777216(即 2²⁴)是 float 能精确表示的最大连续整数;再加 1,可能还是它自己。又比如 20014999 是 8 位整数,float 存出来就变成 2.0015E7 —— 四舍五入已发生。
为什么 0.1f + 0.2f == 0.3f 有时 true,有时 false?
这不是随机,而是不同字面量在 float/double 下的近似值不同:
- 0.1f 和 0.2f 在 float 精度下各自舍入,相加后碰巧与 0.3f 的舍入结果一致 → 返回 true
- 0.3 和 0.6 默认是 double 类型,它们的二进制近似值与 0.9 的 double 近似值不完全对齐 → 0.3 + 0.6 == 0.9 为 false
- 写 0.1 + 0.2 == 0.3 实际比较的是 double 值,而 double 尾数更长(52 位),各数舍入路径不同,结果大概率 false
关键点:别依赖 == 判断浮点相等,这是陷阱入口。
哪些场景必须避开 float/double?
核心判断标准:是否要求「数学意义下的精确相等」或「零误差累积」。
-
绝对禁用:金额计算(0.01 元不能错)、订单编号、数据库主键、配置阈值(如
if (rate == 0.95))、金融报表 -
可以接受:图形坐标(像素级容差)、传感器原始读数(本身含噪声)、归一化系数(
alpha = 0.7f)、模型权重(训练过程允许抖动)
真正靠谱的替代方案
不是“修复 float”,而是换工具:
-
金融/账务逻辑:全程用
BigDecimal,且构造必须用字符串:new BigDecimal("0.1");避免new BigDecimal(0.1)(会把 double 的误差一起带进来) -
数据库层:MySQL 用
DECIMAL(M,D),不是 FLOAT 或 DOUBLE;Oracle 对应NUMBER;确保源头就精确 -
比较浮点数:用误差范围代替
==,例如Math.abs(a - b) -
循环控制:别用
for (float x = 0; x ,改写为整数驱动:<code>for (int i = 0; i - 单位转换法:金额统一存“分”(整数),运算完再除 100 显示;百分比存整数 85 表示 85%,避免 0.85f 的精度干扰
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











