精度丢失的计算过程

moye Lv6
1
2
3
4
5
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 位尾数(或称为小数部分):表示有效数字,使用隐含的第一位。

浮点数使用二进制科学记数法表示,形式为:
$
(-1)^{sign} \times 1.mantissa \times 2^{exponent - 127}
$
其中 sign 表示符号,mantissa 是尾数部分,exponent 是指数。

2. 二进制近似表示

很多十进制小数(如 0.1, 1.9, 1.8)在二进制中是无限循环小数,所以无法用有限位的浮点数精确表示,最终只能存储一个近似值。这就是为什么会出现 0.1000000240.099999905 这样的结果。

3. 计算具体近似值

我们现在来看具体的数值是如何被近似表示的。

1. 2.0f 和 1.9f 的近似表示

  • 2.0f 在二进制中可以精确表示为:
    $
    2.0f = 1.0 \times 2^1 = 01000000000000000000000000000000
    $
    所以没有误差。

  • 1.9f 在二进制中无法精确表示,它的近似表示是:
    $
    1.9f \approx 1.10011001100110011001101_2 \times 2^0
    $
    这个表示法在 float 中近似存储为 0x3fcccccd,实际值大约为 1.89999997615814208984375

2. 计算 2.0f - 1.9f

由于 1.9f 并不是精确的 1.9,而是一个略小的近似值,所以 2.0f - 1.9f 计算的结果并不是 0.1,而是:
$
2.0f - 1.89999997615814208984375 \approx 0.10000002384185791015625
$
由于浮点数只能精确存储有限的位数,最终输出的近似值为 0.100000024

3. 1.8f 和 1.7f 的近似表示

  • 1.8f 在二进制中也无法精确表示,近似表示为:
    $
    1.8f \approx 1.11001100110011001100110_2 \times 2^0
    $
    这个值存储为 0x3fcccccd,近似值大约为 1.80000007152557373046875

  • 1.7f 的二进制表示近似为:
    $
    1.7f \approx 1.10110011001100110011010_2 \times 2^0
    $
    它存储为 0x3fccccc,近似值大约为 1.7000000476837158203125

4. 计算 1.8f - 1.7f

由于 1.8f1.7f 都是近似值,因此它们的差值也会有误差:
$
1.8f - 1.7f \approx 1.80000007152557373046875 - 1.7000000476837158203125 \approx 0.099999904841899871826171875
$
最终输出的近似值为 0.099999905

4. 总结原因

  • 2.0f - 1.9f = 0.100000024 是因为 1.9f 的近似值略小于 1.9,导致差值略大于 0.1。
  • 1.8f - 1.7f = 0.099999905 是因为 1.8f1.7f 都是近似值,它们的差值略小于 0.1。
  • 标题: 精度丢失的计算过程
  • 作者: moye
  • 创建于 : 2024-10-24 14:57:31
  • 更新于 : 2025-12-11 14:39:48
  • 链接: https://www.kanes.top/2024/10/24/精度丢失的计算过程/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论