Java中float和double按IEEE 754标准精确存储,但十进制小数如0.1无法用有限二进制精确表示,导致输入即有误差;float为32位:1位符号、8位偏移指数(Bias=127)、23位隐含前导1的尾数,提供约7位十进制精度;0.1+0.2≠0.3源于二进制表示的固有截断误差,非Java特有;浮点比较应避免==,改用Math.abs(a-b)<ε进行误差容忍判断。

Java 中的 float 和 double 类型严格遵循 IEEE 754 标准存储,不是“近似存”,而是“按规则精确存”——但问题出在十进制小数本身无法被有限二进制准确表达,导致输入阶段就已引入误差。后续所有计算都是对这个已有误差的延续或放大。
IEEE 754 单精度(float)结构拆解
一个 float 占 32 位,分三段:
- 符号位(1 bit):最高位(bit 31),0 表示正,1 表示负
- 指数域(8 bits,bit 30–23):采用偏移码(Bias = 127)。真实指数 = 存储值 − 127。例如存储值 129 → 实际指数为 2;存储值 0 或 255 是特殊状态(零、无穷、NaN、非规格化数)
- 尾数域(23 bits,bit 22–0):只存小数部分,隐含整数位“1.”(规格化数)。即实际尾数是 1.M,提供约 7 位十进制有效数字
为什么 0.1 + 0.2 ≠ 0.3?
根本不在 Java 或 JVM,而在数学层面:
- 0.1₁₀ 在二进制中是无限循环小数:0.0001100110011…₂(循环节 0011)
- float 只能截断保留前 23 位尾数 → 存储的是 0.1 的近似值(如 ≈ 0.10000000149011612)
- 同理,0.2 也被近似 → 两者相加后仍是两个近似值的和,结果自然不是精确的 0.3
- 该现象与语言无关,在 C、Python、JavaScript 中同样发生
浮点比较与判等的正确姿势
直接用 == 判断两个 float/double 是否相等,极易出错:
立即学习“Java免费学习笔记(深入)”;
- 避免写 if (a == b),尤其涉及计算、赋值、序列化/反序列化后
- 改用误差容忍判断:Math.abs(a - b) (根据业务精度选阈值,如金融常用 1e-2)
- 对于必须精确相等的场景(如配置校验、状态标识),优先使用 int 或 long 表达单位(如金额存“分”而非“元”)
- 若需语义上“值相等”,可借助 Double.compare(a, b) == 0,它能正确处理 NaN、±0 等边界
高精度场景:何时以及如何用 BigDecimal
BigDecimal 不是“万能解”,而是有明确适用边界的工具:
- 适合:货币计算、税务、科学常量、审计要求精确小数位的业务
- 不适合:高频数值模拟、图形渲染、机器学习训练(性能开销大,无硬件加速)
- 构造时务必用字符串:new BigDecimal("0.1"),避免 new BigDecimal(0.1)(后者仍从 double 近似值构造)
- 运算统一用方法:add()、subtract()、multiply()、divide(… RoundingMode.HALF_UP),禁用 +−*/ 运算符
- 注意 scale 和 roundingMode:除法必须指定舍入策略,否则可能抛 ArithmeticException


















