精度丢失的计算过程

28 天前(已编辑)
3
摘要
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是精确的浮点数。

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

精度丢失的计算过程

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.1000000240.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.8f1.7f 都是近似值,因此它们的差值也会有误差: 最终输出的近似值为 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...