JavaScript Number Type
在使用Javascript的时候,有一个非常常见的问题:
console.log(0.1 + 0.2 == 0.3) //false
这是为什么呢?根据ECMA262 11th Edition
的6.1.6.1
节:
The Number type has exactly 1843773687445481062 7 R 18437736874454810627_ℝ 18437736874454810627R (that is, 2 R 64 R − 2 R 53 R + 3 R 2_ℝ ^{64ℝ} - 2_ℝ^{53ℝ} + 3_ℝ 2R64R−2R53R+3R) values, representing the double-precision 64-bit format IEEE 754-2019 values as specified in the IEEE Standard for Binary Floating-Point Arithmetic, except that the 9007199254740990ℝ (that is, 2 R 53 R 2_ℝ^{53ℝ} 2R53R - 2 R 2_ℝ 2R) distinct “Not-a-Number” values of the IEEE Standard are represented in ECMAScript as a single special NaN value.
数字类型是根据IEEE754-2019
的中的Double Float
来定义的,它总共有18437736874454810627个值。
Double Float
是由1个符号位,11个指数位,和52个分数位组成的。
它有如下几个特征:
- 符号位只能为0或者1,0代表正数,1代表负数(有+0和-0);
- 指数位为1023时,代表2的0次方;
- 分数为第一位永远为一;
值的储存
为了更加容易理解,这里来举几个例子:
例1: 先来看最简单的1的储存。
首先1是正数,所以符号位应当取0;
其次注意到
1
=
1
b
=
1
∗
2
0
1 = 1b = 1 * 2^0
1=1b=1∗20
所以分数位应当为
100
⋯
0
,
1 00\cdots 0,
100⋯0, 指数位应当为
1023
。
1023。
1023。
例2: 20的储存。
20是正数,所以符号为0;
其次
20
=
10100
b
=
1.01
∗
2
4
20 = 10100b = 1.01*2^4
20=10100b=1.01∗24
所以分数位应为
101
⋯
0
,
101\cdots0,
101⋯0, 指数位应为
1023
+
4
=
1027
。
1023+4 = 1027。
1023+4=1027。
下面来看两个带小数的例子:
例3: 8.25的储存。
符号位为0;
8.25
=
1000.01
b
=
1.00001
∗
2
−
3
8.25 = 1000.01b = 1.00001*2^{-3}
8.25=1000.01b=1.00001∗2−3
所以分数位为
100001
⋯
0
,
100001\cdots0,
100001⋯0, 指数位为
1023
−
3
=
1020
。
1023-3 = 1020。
1023−3=1020。
例4: 回到最初的问题0.1的储存。
符号位为0;
0.1
=
0.000
1
˙
1
˙
0
˙
0
˙
b
=
1.100
1
˙
1
˙
0
˙
0
˙
∗
2
−
4
0.1 = 0.000\dot{1}\dot{1}\dot{0}\dot{0}b = 1.100\dot{1}\dot{1}\dot{0}\dot{0} * 2^{-4}
0.1=0.0001˙1˙0˙0˙b=1.1001˙1˙0˙0˙∗2−4
所以分数位为
110011001100
⋯
,
110011001100\cdots,
110011001100⋯, 指数位为
1023
−
4
=
1019
。
1023-4 = 1019。
1023−4=1019。
至于复数将符号位变为1即可。
由例4我们可以看出,将0.1转化为二进制是有精度损失的,0.2同样会有精度损失,将它们加到一起还会有损失,这也是为什么0.1 + 0.2 不等于 0.3的原因了。
Reference:
EMCA262-Number Type
IEEE-754 2019