[路飞]_面试题_彻底搞懂为什么 JavaScript 0.1 + 0.2 !== 0.3
为什么 0.1 + 0.2 !== 0.3
console.log( 0.1 + 0.2 === 0.3) //false
在做算术运算时,JS 会先把十进制数转换成二进制数后再计算,十进制小数转二进制数的方式是 x 2
取整,0.1 和 0.2 的二进制数是个无限循环小数。
而 JS 中表示一个数字只有 64 位,其中精度位(有效数位)只有 52 位,所以当出现无限循环小数时,会通过 0 舍 1 入 的规则截取前 52 位(类似十进制的四舍五入),这样导致了精度位的丢失。0.1 实际参与计算的数变大了,0.2 参与计算的数变小了,所以运算结果不一定等于 0.3。
0.1 + 0.2 多出的 0.0...4 是从哪里来的
console.log( 0.1 + 0.2) //0.30000000000000004
0.1 转二进制计算
0.1 的二进制数是无限循环小数: 0.00011001100110011001100110011001100110011001100110011001...
第 53 位是 1,在截取前 52 位时 0 舍 1 入后实际参与运算的是: 0.0001100110011001100110011001100110011001100110011010
0.2 转二进制计算
0.2 的二进制数是无限循环小数: 0.00110011001100110011001100110011001100110011001100110011...
第 53 位是 0,在截取前 52 位时 0 舍 1 入后实际参与运算的是: 0.0011001100110011001100110011001100110011001100110011
多出的 0.0...4
所以 0.0...4 是在转二进制截取时 0 舍 1 入 0.1 入的部分和 0.2 舍去部分之和。
为什么 0.1 + 0.1 === 0.2
0.1 转二进制后是个近似值,0.2 转二进制后也是个近似值,两个近似值相加后的二进制数碰巧等于另一个近似值。
这里只是碰巧相等,浮点数的比较方式还是不能通过 === 号比较。
正确的浮点数比较方式
正确的浮点数比较方式是比较绝对值是否在 JS 提供的最小精度范围内
console.log( Math.abs(0.1 + 0.2 - 0.3) <= Number.EPSILON) //true
JavaScript 如何表示一个数字
双精度浮点数
JS 中的 Number 类型基本符合 IEEE 754-2008 规定的双精度浮点数规则,IEEE 可以读作 I treble E。根据浮点数的定义,双精度浮点数由 64 位组成,如下图:(这里的位是指二进制,0 或 1表示)
64 位分别为:
- 符号位 1 位:不参与计算,0 表示正数 1 表示负数;
- 指数位 11 位:有个基准值 01111111111,小于这个数时指数位就是负数。因为指数位没有符号位,而科学计数法的指数位可以为负数。转换的时候要减去这个基准值;
- 有效位数 52 位:有效数字隐藏了个 1,因为以 0 开头没有意义;
有效位数决定了这个数的精度,指数位表示浮点数表示的范围。浮点数可以表示很大的数,在表示最大数的时候,可能不是每个整数都能表示了,数越大能表示的数就越稀疏,因为有效位数精度会丢失。64 位二进制位转十进制公式为:
- 十进制数字 = 符号位 + 有效数位 x 2^(指数-1023)次方
上文中的指数位、有效位数对应的是科学计数法。十进制数转换成二进制后,再转换成二进制的科学计数法数字,从而得到指数位、有效数位。
10 进制数转 64 位浮点数的过程
以十进制数 12.25 为例,转换成 64 位二进制过程如下:
- 第一步,将整数部分 12 转换成二进制得到 1100;
- 第二步,将小数部分 0.25 转换成二进制得到 0.01;
- 第三步,合并结果为 1100.01;
- 第四步,将二进制数转换成科学计数法,得到指数位和有效数位
- 根据科学计数法规则,整数部分保留 1 位,所以小数点往左移动 3 位,得到指数位 3 和有效数位 1.10001
- 第五步,确定双精度浮点数每一位的值
- 符号位 1 位:0 正数
- 指数位 11 位:3 -> 00000000011 -> 00000000011 + 01111111111(基准值)-> 10000000010
- 有效数位 52 位:1.10001 -> 1.1000100000000000000000000000000000000000000000000000 -> 1000100000000000000000000000000000000000000000000000,第一位是 1 隐藏了。
- 第六步,合并每位结果为:0100000000101000100000000000000000000000000000000000000000000000
进制转换
十进制小数如何转二进制
转换规则为 x 2
取整,十进制数 0.8125 = 0.11101,转二进制过程如下:
十进制数 0.2 = 0.0011001100110011001100110011001100110011001100110011 ...,从小数点第 5 位开始进入无限循环,转二进制过程如下:
十进制整数数如何转二进制浮点数
转换规则为 ÷ 2
取余,十进制数 125 = 1111101,转二进制过程如下:
推荐转换工具:
如有错误欢迎指出,欢迎一起讨论!
转载自:https://juejin.cn/post/7051434298926170148