在开发过程中遇到类似的问题:

  1. let n1 = 0.1, n2 = 0.2;
  2. console.log(n1 + n2) // 0.30000000000000004
  1. 通常得到的不是想要的结果,要想等于0.3,就要把它进行转化:
  1. (n1 + n2).toFixed(2) // toFixed为四舍五入
  1. 那么出现上述的结果的原因是什么呢?<br />计算机是通过二进制的方式来存储数据,二进制只能精准表达能被2除尽的数字,在二进制无法精准表示时,需要根据精度舍入(`0舍1入`)。所以当计算机在计算`0.1 + 0.2`的时候,实际上是在计算两个数的二进制的和。0.1的二进制是0.0001100110011001100...(1100循环),0.2的二进制为0.00110011001100...(1100循环),这两个数的二进制都是无限循环的数。<br />由于Number数据类型都是采用double双精度浮点数,我们来看看双精度数是如何保存的:<br />![](https://cdn.nlark.com/yuque/0/2022/jpeg/28976061/1659679115731-592c134a-92a4-4c0c-9b53-f2f1dfe016d1.jpeg)
  1. sign(蓝色):用来存储符号位、区分正负数,0表示正数,占用1位;
  2. exponent(绿色):用来存储指数,占用11位;
  3. fraction(红色):用来存储小数,占用52位。

当我们将0.1的二进制转为科学计数法时,得到下述结果:

  1. 1.1001100110011001100110011001100110011001100110011001*2^-4

可以从中看出0.1的符号位为0,指数为-4,小数位为:

  1. 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

  1. function numberepsilon(arg1,arg2){
  2. return Math.abs(arg1 - arg2) < Number.EPSILON;
  3. }
  4. console.log(numberepsilon(0.1 + 0.2, 0.3)); // true