❗️前端尽量不计算,特别是计算💰
看下面的例子,实际开发中,JS计算加减乘除都可能得到不精确值,发生我们常说的丢精度的问题。
console.log(0.1 + 0.2) // 0.30000000000000004
console.log(1.5 - 1.2) // 0.30000000000000004
console.log(19.9 * 100) // 1989.9999999999998
console.log(0.69 / 10) // 0.06899999999999999
我们按顺序来了解下面几个问题:
- JS是怎么存储数字的
- 计算的时候,JS是怎么读取数字的
- 读取后,JS是如何计算的
(1)JS是怎么存储数字的
JS中数字遵循 IEEE 754标准,使用64位双精度浮点数来表示数字。
64位双精度浮点数,由3部分组成:
- 符号位S:1位,用来表示正负号。(1为负,0为正)
- 指数位E:11位,用来表示次方数。
- 尾数位M:52位,二进制有效数字,用来表示精确度。超过52位的部分自动进1舍0.
64位双精度浮点数,用二进制科学计数法表示,如下:
比如 8.25(十进制浮点数)
转换为`二进制浮点数`:1000.01
使用`二进制科学计数法`表示:1.00001*(2^3)
解析变量:S=0, M=0.00001, E=1026(10000000010)
所以,存储为64位双精度浮点数:0(S)10000000010(E)00001(M) = 0100 0000 0010 0000 1000 ……(11个0000)
PS:十进制转二进制规则:整数除2取余,逆序排列;小数乘2取整,顺序排列。
(2)计算的时候,JS是怎么读取数字的
还是以 8.25 为例
内存存储的64位双精度浮点数:0100 0000 0010 0000 1000 ……(11个0000)
1、划分出SEM值:0(S)10000000010(E)00001(M)
2、转为二进制科学计数:
(-1)^S = 1
1 + M = 1.00001
E=10000000010,转为10进制是2026
E-2023 = 3
计算为1.00001*2^3 = 1000.01
3、转为10进制浮点数:8.25
(3)JS是如何计算的
举个例子: 0.1 + 0.2 = 0.30000000000000004
0.1,0.2存储为64位二进制浮点数如下图:
0.1 => S=0,E=01111111011(1019),M=1001 10011001 10011001 10011001 10011001 10011001 10011010 (超出52位的丢弃,10011001进1舍0)
0.2 => S=0,E=01111111100(1020),M=1001 10011001 10011001 10011001 10011001 10011001 10011010 (超出52位的丢弃,10011001进1舍0)
读取两个数,并转为可以计算的二进制数:
0.1=> 1.1001 10011001 10011001 10011001 10011001 10011001 10011010 * 2^(-4) => 0.00011001 10011001 10011001 10011001 10011001 10011001 10011010
0.2=> 1.1001 10011001 10011001 10011001 10011001 10011001 10011010 * 2^(-3) => 0.0011001 10011001 10011001 10011001 10011001 10011001 10011010
相加计算:
0.1=>0.00011001100110011001100110011001100110011001100110011010
0.2=>0.0011001100110011001100110011001100110011001100110011010
+=>0.0100110011001100110011001100110011001100110011001100111
计算得到的64位双精度浮点数,转为十进制浮点数,结果如图所示:
可以看出来,在JS存储的时候精度就丢失了,后面都是在不精准的值上计算的。
(4)如何让 0.1+0.2 == 0.3
- 使用
toFixed方法对结果四舍五入 - 使用
Number.EPSILON属性
Number.EPSILON:ES6引入的新属性,用于比较浮点数之间的精度。它表示比1大的最小浮点数与1之间的绝对差值。
if(Math.abs(0.1+0.2-0.3) < Number.EPLISON){ // 则判断 0.1+0.2 = 0.3}
- 使用社区提供一些成熟的库,比如 bignumber.js,decimal.js,以及 big.js 等
因为一个bug,总结了这篇文章。如果有错误的话欢迎大家帮忙指正嗷!!!谢谢大家~~~