- 为什么 0.1 + 0.2 !== 0.3?
- 为什么 Number.MAX_SAFE_INTEGER 值为 Math.pow(2, 53) - 1?
- 为什么 Math.pow(2, 53) === Math.pow(2, 53) + 1 ?
- 为什么 Number.MAX_VALUE = 1.7976931348623157e+308 ?
- 为什么 1.005.toFixed(2) = 1.00 ?
1. 浮点数二进制的存储结构
下图是单精度格式

对于双精度浮点数,采用64位存储,最高的1位是符号位S,接着的11位是指数E(-1022 ~ 1023),剩下的52位为有效数字尾数M。
为什么E(双精度)范围是:-1022 ~ 1023 ?
// E 的范围
E = 0 二进制表示: 0 1111 1111 11 => 2^10 -1 = 1023
E 最大二进制表示(不能全为1):1 1111 1111 10 => 2^11 - 2 = 2046
E 最小二进制表示(不能全为0): 0 0000 0000 01 => 2^0 = 1
则E的范围(1-1023)<= E (2046 - 1023)
2. 任何数都可以表示为二进制:1.xxx * 2^e(这是一个规格化的表示),
十进制浮点数转二进制, eg:
0.125
x 2 ----
----------------- |
0.25 0 |
x 2 |
----------------- |
0.5 0 |
x 2 |
----------------- |
1.0 1 ↓
13.125, 整数部分13 => 1101, 小数部分 0.125 => 0.001
13.125 => 1101.001 => 1.101001 * 2^3(存储时,小数点前的1省略了,节省空间)
// 以上S=0; E=3(二进制表示为:10000000010); M=101001,
则二进制表示为:
0 10000000010 1010010000000000000000...
- ----------- -------------------------
s E(1023+3) M,52位长度
0.1 转化位二进制: 0.0001100110011(0011循环) => 1.100110011(0011)*2^-4
0.2 转化位二进制: 0.001100110011(0011循环) => 1.100110011(0011)*2^-3
3. 运算
- 对阶: 小阶码与大阶码对齐
0.1:1.100110011(0011) * 2^-4 = 0.1100110011(0011) * 2^-3
0.2:1.100110011(0011)*2^-3 - 尾数就近舍入:
a. 多余位 <= 011...11, 舍去。
b. 多余位 = 100...00, 判断尾数的最低有效位的值,若为0则直接舍去,若为1则再加1。
c. 多余位 >= 100...01, 进1。
0.1 => 0.1100110011001100110011001100110011001100110011001100[1100110011...] => 进1 => 0.1100110011001100110011001100110011001100110011001101
0.2 => 1.1001100110011001100110011001100110011001100110011001[100110011...] => 进1 => 0.1100110011001100110011001100110011001100110011001110
0.1100110011001100110011001100110011001100110011001101 * 2^-3
+ 1.1100110011001100110011001100110011001100110011001110 * 2^-3
----------------------------------------------------------
10.0110011001100110011001100110011001100110011001100111 * 2^-3
=> 1.00110011001100110011001100110011001100110011001100111 * 2^-2
=> 1.0011001100110011001100110011001100110011001100110100 * 2^-2(由于尾数只能52位,就近舍入)
转化为10进制
0.3000000000000000444089209850062616169452667236328125
=> 0.30000000000000004
4. 解惑
- 为什么 0.1 + 0.2 !== 0.3?
a. 搞清楚number的存储结构;b. 知道十进制二进制之间的转换;c. 搞清楚 运算过程。 - 为什么 Number.MAX_SAFE_INTEGER 值为 Math.pow(2, 53) - 1?
因为二进制表示:1.xxx * 2^e , 尾数xxx最多52位,则e<=52时是准确的,超出则会有精度损失,则二进制53个1等于Math.pow(2, 53) - 1。 - 为什么 Math.pow(2, 53) === Math.pow(2, 53) + 1 ?
因为1(十进制) => 1 * 2^0(二进制) => 0.000...1 * 2^53(尾数共52个0,一个1,尾数就近舍入) === 0
- (2^53, 2^54) 之间的数会两个选一个,只能精确表示偶数
- (2^54, 2^55) 之间的数会四个选一个,只能精确表示4个倍数
- ... 依次跳过更多2的倍数
Math.pow(2, 53) + 3 === Math.pow(2, 53) + 5 // true
Math.pow(2, 54) === Math.pow(2, 54) + 1 // true
Math.pow(2, 54) === Math.pow(2, 54) + 2 // true
- 为什么 Number.MAX_VALUE = 1.7976931348623157e+308 ? 最大整数为 Math.pow(2, 1024) - 1, 但是2^1024 为 Infinity。
Math.pow(2, 1024) // Infinity
Math.pow(2, 1023) * 1.999999999999999 // 1.797693134862315e+308
- 为什么 1.005.toFixed(2) = 1.00 ?
看山不是山,1.005 实际对应的数字是 1.00499999999999989,在四舍五入时全部被舍去!toPrecision函数可查看精度。
