精度丢失的计算过程

2024 年 10 月 24 日 星期四(已编辑)
5
摘要
IEEE 754单精度浮点数标准规定了float在Java中占用32位,包括1位符号位、8位指数和23位尾数。由于二进制浮点数表示的限制,很多十进制小数在二进制中是无限循环小数,导致浮点数只能存储近似值。具体计算中,2.0f和1.9f的近似表示分别导致2.0f-1.9f结果为0.100000024,1.8f和1.7f的近似表示导致1.8f-1.7f结果为0.099999905。这是因为1.9f和1.8f在二进制中无法精确表示,而1.7f和2.0f是精确的浮点数。
这篇文章上次修改于 2024 年 10 月 24 日 星期四,可能部分内容已经不适用,如有疑问可询问作者。

阅读此文章之前,你可能需要首先阅读以下的文章才能更好的理解上下文。

精度丢失的计算过程

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×1.mantissa×2exponent127(-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×21=010000000000000000000000000000002.0f = 1.0 \times 2^1 = 01000000000000000000000000000000 所以没有误差。

  • 1.9f 在二进制中无法精确表示,它的近似表示是: 1.9f1.100110011001100110011012×201.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.0f1.899999976158142089843750.100000023841857910156252.0f - 1.89999997615814208984375 \approx 0.10000002384185791015625 由于浮点数只能精确存储有限的位数,最终输出的近似值为 0.100000024

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

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

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

4. 计算 1.8f - 1.7f

由于 1.8f1.7f 都是近似值,因此它们的差值也会有误差: 1.8f1.7f1.800000071525573730468751.70000004768371582031250.0999999048418998718261718751.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。

使用社交账号登录

  • Loading...
  • Loading...
  • Loading...
  • Loading...
  • Loading...