精度丢失的计算过程
float a = 2.0f - 1.9f;
float b = 1.8f - 1.7f;
System.out.println(a);// 0.100000024
System.out.println(b);// 0.099999905
System.out.println(a == b);// false
1. IEEE 754 单精度浮点数标准
单精度浮点数 (float
) 在 Java 中占用 32 位,分为三个部分:
- 1 位符号位:表示正数或负数。
- 8 位指数:表示二进制科学记数法中的指数部分。
- 23 位尾数(或称为小数部分):表示有效数字,使用隐含的第一位。
浮点数使用二进制科学记数法表示,形式为:
其中 sign
表示符号,mantissa
是尾数部分,exponent
是指数。
2. 二进制近似表示
很多十进制小数(如 0.1, 1.9, 1.8)在二进制中是无限循环小数,所以无法用有限位的浮点数精确表示,最终只能存储一个近似值。这就是为什么会出现 0.100000024
和 0.099999905
这样的结果。
3. 计算具体近似值
我们现在来看具体的数值是如何被近似表示的。
1. 2.0f 和 1.9f 的近似表示
2.0f 在二进制中可以精确表示为: 所以没有误差。
1.9f 在二进制中无法精确表示,它的近似表示是: 这个表示法在
float
中近似存储为0x3fcccccd
,实际值大约为1.89999997615814208984375
。
2. 计算 2.0f - 1.9f
由于 1.9f
并不是精确的 1.9,而是一个略小的近似值,所以 2.0f - 1.9f
计算的结果并不是 0.1,而是:
由于浮点数只能精确存储有限的位数,最终输出的近似值为 0.100000024
。
3. 1.8f 和 1.7f 的近似表示
1.8f 在二进制中也无法精确表示,近似表示为: 这个值存储为
0x3fcccccd
,近似值大约为1.80000007152557373046875
。1.7f 的二进制表示近似为: 它存储为
0x3fccccc
,近似值大约为1.7000000476837158203125
。
4. 计算 1.8f - 1.7f
由于 1.8f
和 1.7f
都是近似值,因此它们的差值也会有误差:
最终输出的近似值为 0.099999905
。
4. 总结原因
- 2.0f - 1.9f = 0.100000024 是因为
1.9f
的近似值略小于 1.9,导致差值略大于 0.1。 - 1.8f - 1.7f = 0.099999905 是因为
1.8f
和1.7f
都是近似值,它们的差值略小于 0.1。