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