JS面试----为什么0.1 + 0.2 != 0.3?如何解决这个问题

0.1 + 0.2 != 0.3?

我们先来看一个例子

console.log(0.1 + 0.2)  // 结果是0.30000000000000004,而不是0.3
console.log(0.2 - 0.1) // 0.1

这是为什么呢?

要弄清这个问题的原因,首先需要我们了解在计算机中数字是如何存储和运算的。在计算机中,无论是定点数还是浮点数都是以多位二进制的方式进行存储的。而JS中采用的是IEEE 754的双精度版本(64位),所以只要采用IEEE 754的语言都会有该问题。我们先来简单看一下存储的结构:

IEEE 754双精度(64位)浮点数的结构:

其中:

名称                                       长度                  比特位置

符号位 Sign (S)                       1bit                     (b63)

指数部分 Exponent (E)           11bit                  (b62-b52)

尾数部分 Mantissa (M)            52bit                  (b51-b0)

双进度的指数部分(E)采用的偏置码为1023

求值方法:(-1)^s  * (1.M) * 2^(E-1023)

JS中0.1的表示

我们来看0.1在二进制中会表示为什么呢

// 0.1 二进制(0011)表示循环
0.1 = 0.0001100110011(0011)
0.1 = 2^-4 * 1.10011(0011)

我们发现,0.1在二进制中是无限循环的一些数字,其实不止0.1,很多十进制小数用二进制表示都是无限循环的。这样其实没什么问题,但是JS 采用的浮点数标准却会裁剪掉我们的数字。也就是说0.1在IEEE 754下位数超出52位后,循环的0011会被裁剪掉,就会出现精度丢失的问题。也就造成了 0.1 不再是 0.1 了,而是变成了 0.100000000000000002

0.100000000000000002 === 0.1 // true

那么同样的,0.2 在二进制也是无限循环的,被裁剪后也失去了精度变成了 0.200000000000000002

0.200000000000000002 === 0.2 // true

所以这两者相加不等于 0.3 而是 0.300000000000000004

0.1 + 0.2 === 0.30000000000000004 // true

那么既然0.1不是0.1,那为什么console.log(0.1)却是正确的?

因为在输入内容的时候,二进制被转换为了十进制,而十进制又被转换为字符串,在这个转换的过程中发生了取近似值的过程,所以打印出来的其实是一个近似值。原生提供的方式就是用过toFixed()方法:

 解决0.1 + 0.2 != 0.3的问题

所以解决0.1 + 0.2 != 0.3的办法,用原生提供的方式就是

parseFloat((0.1 + 0.2).toFixed(10)) === 0.3 // true

ES 6在Number对象上新增了一个极小的常量----Number.EPSILON, 引入这个极小常量的目的是在于为浮点数计算设置一个误差范围,如果两个数的误差能够小于Number.EPSILON,我们就认为两个数相等。

0.1 + 0.2 - 0.3 < Number.EPSILON // true

猜你喜欢

转载自www.cnblogs.com/jett-woo/p/12566682.html
今日推荐