在开发过程中遇到类似的问题:
let n1 = 0.1, n2 = 0.2;
console.log(n1 + n2) // 0.30000000000000004
通常得到的不是想要的结果,要想等于0.3,就要把它进行转化:
(n1 + n2).toFixed(2) // toFixed为四舍五入
那么出现上述的结果的原因是什么呢?<br />计算机是通过二进制的方式来存储数据,二进制只能精准表达能被2除尽的数字,在二进制无法精准表示时,需要根据精度舍入(`0舍1入`)。所以当计算机在计算`0.1 + 0.2`的时候,实际上是在计算两个数的二进制的和。0.1的二进制是0.0001100110011001100...(1100循环),0.2的二进制为0.00110011001100...(1100循环),这两个数的二进制都是无限循环的数。<br />由于Number数据类型都是采用double双精度浮点数,我们来看看双精度数是如何保存的:<br />
- sign(蓝色):用来存储符号位、区分正负数,0表示正数,占用1位;
- exponent(绿色):用来存储指数,占用11位;
- fraction(红色):用来存储小数,占用52位。
当我们将0.1
的二进制转为科学计数法时,得到下述结果:
1.1001100110011001100110011001100110011001100110011001*2^-4
可以从中看出0.1
的符号位为0,指数为-4,小数位为:
1001100110011001100110011001100110011001100110011001
当指数位为负数时,IEEE标准规定了一个偏移量,对于指数部分,每次都加上这个偏移量来进行保存,这样即使指数是负数,加上偏移量后也会是正数了。
- 当指数位不全是0也不全是1时(规格化的数值旬,IEEE规定,阶码计算公式为e-Bias。此时e
最小值是1,则1-1023=-1022,e最大值是2046,则2046-1023=1023,可以看到,这种
情况下取值范围是-1022~1023。
- 当指数位全部是0的时候(非规格化的数值,IEEE规定,阶码的计算公式为1-Bias,即1-1023=-1022.
- 当指数位全部是1的时候(特殊值,EEE规定这个浮点数可用来表示3个特殊值,分别是正无
穷,负无穷,NaN。具体的,小数位不为0的时候表示NaN;小数位为O时,当符号位s=0时
表示正无穷,$=1时候表示负无穷。
所以对于0.党的指数位为-4, -4+1023=1019,转为二进制即1111111011
.
解决办法
对于这个问题,可以设置一个误差范围,通常称为“机器精度”。在ES6中提供了Number.EPSILON
属性,它的值为2 - 52。我们只要判断0.1+0.2-0.3
是否小于Number.EPSILON
,如果小于即可判断0.1+0.2 === 0.3
function numberepsilon(arg1,arg2){
return Math.abs(arg1 - arg2) < Number.EPSILON;
}
console.log(numberepsilon(0.1 + 0.2, 0.3)); // true