阅读完本文可以了解到
0.1
+
0.2
为什么等于
0.30000000000000004
以及 JavaScript 中最大安全数是如何来的。
十进制小数转为二进制小数方法
拿 173.8125 举例如何将之转化为二进制小数。
① 针对整数部分 173,采取
除
2
取余,逆序排列
。
173 / 2 = 86 ... 1
86 / 2 = 43 ... 0
43 / 2 = 21 ... 1 ↑
21 / 2 = 10 ... 1 | 逆序排列
10 /
2 = 5 ... 0 |
5 / 2 = 2 ... 1 |
2 / 2 = 1 ... 0
1 / 2 = 0 ... 1
得整数部分的二进制为
10101101
。
② 针对小数部分 0.8125,采用
乘
2
取整,顺序排列
。
0.8125 * 2 = 1.625 |
0.625 * 2 = 1.25 | 顺序排列
0.25
* 2 = 0.5 |
0.5 * 2 = 1 ↓
得小数部分的二进制为
1101
。
③ 将前面两部的结果相加,结果为
10101101.1101
。
小心,二进制小数丢失了精度!
根据上面的知识,将十进制小数
0.1
转为二进制:
0.1 * 2 = 0.2
0.2 * 2 = 0.4 // 注意这里
0.4 * 2 = 0.8
0.8 * 2 = 1.6
0.6 * 2 = 1.2
0.2
* 2 = 0.4 // 注意这里,循环开始
0.4 * 2 = 0.8
0.8 * 2 = 1.6
0.6 * 2 = 1.2
...
可以发现有限十进制小数
0.1
却转化成了无限二进制小数
0.00011001100
...
,可以看到精度在转化过程中丢失了!
能被转化为有限二进制小数的十进制小数的最后一位必然以 5 结尾(因为只有 0.5 * 2 才能变为整数)。所以十进制中一位小数
0.1
~
0.9
当中除了
0.5
之外的值在转化成二进制的过程中都丢失了精度。
推导 0.1 + 0.2 为何等于 0.30000000000000004
在 JavaScript 中所有数值都以 IEEE-754 标准的
64
bit
双精度浮点数进行存储的。先来了解下 IEEE-754 标准下的双精度浮点数。
这幅图很关键,可以从图中看到 IEEE-754 标准下双精度浮点数由三部分组成,分别如下:
推荐阅读 JavaScript 浮点数陷阱及解法,阅读完该文后可以了解到以下公式的由来。
精度位总共是 53 bit,因为用科学计数法表示,所以首位固定的 1 就没有占用空间。即公式中 (M + 1) 里的 1。另外公式里的 1023 是 2^11 的一半。小于 1023 的用来表示小数,大于 1023 的用来表示整数。
指数可以控制到 2^1024 - 1,而精度最大只达到 2^53 - 1,两者相比可以得出 JavaScript 实际可以精确表示的数字其实很少。
0.1
转化为二进制为
0.0001100110011
...
,用科学计数法表示为
1.100110011
...
x
2
^(-
4
)
,根据上述公式,
S
为
0
(1 bit),
E
为
-
4
+
1023
,对应的二进制为
01111111011
(11 bit),
M
为
1001100110011001100110011001100110011001100110011010
(52 bit,另外注意末尾的进位),
0.1
的存储示意图如下:
同理,
0.2
转化为二进制为
0.001100110011
...
,用科学计数法表示为
1.100110011
...
x
2
^(-
3
)
,根据上述公式,
E
为
-
3
+
1023
,对应的二进制为
01111111100
,
M
为
1001100110011001100110011001100110011001100110011010
,
0.2
的存储示意图如下:
0.1
+
0.2
即 2^(-4) x 1.1001100110011001100110011001100110011001100110011010 与 2^(-3) x 1.1001100110011001100110011001100110011001100110011010 之和
// 计算过程
0.00011001100110011001100110011001100110011001100110011010
0.0011001100110011001100110011001100110011001100110011010
// 相加得
0.01001100110011001100110011001100110011001100110011001110
0.01001100110011001100110011001100110011001100110011001110
转化为十进制就是
0.30000000000000004
。验证完成!
JavaScript 的最大安全数是如何来的
根据双精度浮点数的构成,精度位数是
53
bit
。安全数的意思是在
-
2
^
53
~