15.1. 表示性错误

本小节将详细解释 "0.1" 的例子,并说明你可以怎样亲自对此类情况进行精确分析。 假定前提是已基本熟悉二进制浮点表示法。

表示性错误 是指某些(其实是大多数)十进制小数无法以二进制(以 2 为基数的计数制)精确表示这一事实造成的错误。 这就是为什么 Python(或者 Perl、C、C++、Java、Fortran 以及许多其他语言)经常不会显示你所期待的精确十进制数值的主要原因。

为什么会这样? 1/10 是无法用二进制小数精确表示的。 目前(2000年11月)几乎所有使用 IEEE-754 浮点运算标准的机器以及几乎所有系统平台都会将 Python 浮点数映射为 IEEE-754 “双精度类型”。 754 双精度类型包含 53 位精度,因此在输入时,计算会尽量将 0.1 转换为以 J/2**N 形式所能表示的最接近分数,其中 J 为恰好包含 53 个二进制位的整数。 重新将

  1. 1 / 10 ~= J / (2**N)

写为

  1. J ~= 2**N / 10

并且由于 J 恰好有 53 位 (即 >= 252< 253),N 的最佳值为 56:

  1. >>> 2**52 <= 2**56 // 10 < 2**53
  2. True

也就是说,56 是唯一的 N 值能令 J 恰好有 53 位。 这样 J 的最佳可能值就是经过舍入的商:

  1. >>> q, r = divmod(2**56, 10)
  2. >>> r
  3. 6

由于余数超过 10 的一半,最佳近似值可通过四舍五入获得:

  1. >>> q+1
  2. 7205759403792794

这样在 754 双精度下 1/10 的最佳近似值为:

  1. 7205759403792794 / 2 ** 56

分子和分母都除以二则结果小数为:

  1. 3602879701896397 / 2 ** 55

请注意由于我们做了向上舍入,这个结果实际上略大于 1/10;如果我们没有向上舍入,则商将会略小于 1/10。 但无论如何它都不会是 精确的 1/10!

因此计算永远不会“看到”1/10:它实际看到的就是上面所给出的小数,它所能达到的最佳 754 双精度近似值:

  1. >>> 0.1 * 2 ** 55
  2. 3602879701896397.0

如果我们将该小数乘以 10**55,我们可以看到该值输出为 55 位的十进制数:

  1. >>> 3602879701896397 * 10 ** 55 // 2 ** 55
  2. 1000000000000000055511151231257827021181583404541015625

这意味着存储在计算机中的确切数值等于十进制数值 0.1000000000000000055511151231257827021181583404541015625。 许多语言(包括较旧版本的 Python)都不会显示这个完整的十进制数值,而是将结果舍入为 17 位有效数字:

  1. >>> format(0.1, '.17f')
  2. '0.10000000000000001'

fractionsdecimal 模块可令进行此类计算更加容易:

  1. >>> from decimal import Decimal
  2. >>> from fractions import Fraction
  3.  
  4. >>> Fraction.from_float(0.1)
  5. Fraction(3602879701896397, 36028797018963968)
  6.  
  7. >>> (0.1).as_integer_ratio()
  8. (3602879701896397, 36028797018963968)
  9.  
  10. >>> Decimal.from_float(0.1)
  11. Decimal('0.1000000000000000055511151231257827021181583404541015625')
  12.  
  13. >>> format(Decimal.from_float(0.1), '.17')
  14. '0.10000000000000001'