舍入误差

1 引例

先举个栗子:

var a=2;
var b=1.7;
console.log(a-b);

打印结果是0.30000000000000004

2 概念

上述现象叫做计算机中的舍入误差。原本关于舍入误差,我只记得一句话:计算机底层运算时二进制的,无法精确表示0.1这个十进制数,所以会造成舍入误差。今天逛知乎看到关于这个问题的精彩答案,觉得有必要摘过来存下~感谢作者Adam Wen。

3 原理

不管是什么数, 在计算机中最终都会被转化为 0 和 1 进行存储, 所以需要弄明白以下几点问题:

  • 一个小数如何转化为二进制
  • 浮点数的二进制如何存储

浮点数的二进制表示

首先我们要了解浮点数二进制表示, 有以下两个原则:

  • 整数部分对 2 取余然后逆序排列
  • 小数部分乘 2 取整数部分, 然后顺序排列

0.1 的表示是什么?

我们继续按照浮点数的二进制表示来计算

  • 0.1 * 2 = 0.2 整数部分取 0
  • 0.2 * 2 = 0.4 整数部分取 0
  • 0.4 * 2 = 0.8 整数部分取 0
  • 0.8 * 2 = 1.6 整数部分取 1
  • 0.6 * 2 = 1.2 整数部分取 1
  • 0.2 * 2 = 0.4 整数部分取 0

所以你会发现, 0.1 的二进制表示是 0.00011001100110011001100110011……0011
0011作为二进制小数的循环节不断的进行循环.

这就引出了一个问题, 你永远不能存下 0.1 的二进制, 即使你把全世界的硬盘都放在一起, 也存不下 0.1 的二进制小数.

浮点数的二进制存储

Python 和 C 一样, 采用 IEEE 754 规范来存储浮点数. IEEE 754 对双精度浮点数的存储规范将 64 bit 分为 3 部分.

第 1 bit 位用来存储 符号, 决定这个数是正数还是负数
然后使用 11 bit 来存储指数部分
剩下的 52 bit 用来存储尾数
Double-precision_floating-point_format

而且可以指出的是, double 能存储的数的个数是有限的, double 能代表的数必然不超过 2^64 个, 那么现实世界上有多少个小数呢? 无限个. 计算机能做的只能是一个接近这个小数的值, 是这个值在一定精度下与逻辑认为的值相等. 换句话说, 每个小数的存储(但是不是所有的), 都会伴有精度的丢失.

浮点数计算的问题

0.1 + 0.2 == 0.3

0.1 在计算机存储中真正的数字是0.1000000000000000055511151231257827021181583404541015625,0.2 是0.200000000000000011102230246251565404236316680908203125,0.3 是0.299999999999999988897769753748434595763683319091796875

这就是为什么 0.1 + 0.2 != 0.3 的原因

点这里查看原文By Adam Wen From ZhiHu.

热评文章