JavaScript中0.1+0.2==0.3返回false

题目

JavaScript:
0.1+0.2 == 0.3 // false
0.7*180 === 125.99999999999999 // true
1000000000000000128 === 1000000000000000129 // true

Python也有这个问题


JavaScript中0.1+0.2==0.3返回false_第1张图片
Python浮点数相加

不仅是JavaScript和Python,所有遵循IEEE754标准的语言中,0.1+0.2 !== 0.3; 0.2+0.3 === 0.5

单精度,双精度

IEEE754中浮点数值的表示是:单精度(32位)和双精度(64位),JavaScript中采用双精度浮点数。

JavaScript如何表示数字

JavaScript不区分整数和浮点数,遵循IEEE754标准,64位(bit)双精度浮点数编码,所以JS中所有的数字都是浮点数。

浮点数的特点

  • 浮点数可以表示的值的范围比同等位数的整数表示方法的值的范围大得多
  • 浮点数无法精确的表示其范围内所有的数值;有符号和无符号整数可以表示其范围内的每一个值。
  • 浮点数的个数是无限的,导致JavaScript不能精确表示所有的浮点数,只能是一个近似值。

一个字节===8位

数字在内存中的表示

JavaScript中0.1+0.2==0.3返回false_第2张图片
数字在内存中的表示

说明:

  • 0位:符号位,0代表正数,1代表负数
  • 1-11位:存储指数部分(e)
  • 12-63位,存储小数部分(有效数字部分)

js 的最大安全数:
整数部分只有52位,而最大安全数是(2的53次方-1),为什么?
因为:二进制表示有效数字时,以1.xxx...xxxx的形式表示,尾数部分在规定形式下第一位默认是1(省略不写,xxx...xxxx是尾数部分,最长为52位,)。因此,JavaScript有效数字最长是53位二进制位。

Number.MAX_SAFE_INTEGER === 9007199254740991
Math.pow(2,53)-1 === 9007199254740991
Number.MAX_SAFE_INTEGER === Math.pow(2,53)-1 // true

浮点数是怎么运算的

计算机不能对十进制的数字直接运算,要先按照IEEE754转成二进制,再对阶运算

转成二进制
0.1和0.2转换成二进制后会无限循环

0.1 => 0.0001100110011001100110011001100110011001100110011001101......(无限循环的)
0.2 => 0.001100110011001100110011001100110011001100110011001101......(无限循环的)
JavaScript中0.1+0.2==0.3返回false_第3张图片

由于IEEE754尾数位数的限制,二进制后边的就会被截掉。所以说,在十进制转二进制的过程中,精度就已经损失了。

0.1为什么等于0.1

0.1为什么等于0.1

标准中规定尾数f的固定长度是52位,再加上省略的一位,这53位是JS精度范围。它最大可以表示2^53(9007199254740992), 长度是 16,所以可以使用 toPrecision(16) 来做精度运算,超过的精度会自动做凑整处理

0.10000000000000000555.toPrecision(16) // "0.1000000000000000"

0.1.toPrecision(16)  // "0.1000000000000000"
0.1.toPrecision(23)  // "0.10000000000000000555112"

所以,0.1===0.1是因为超过16位后自动凑整,导致他俩相等了。

对阶运算

对阶:指将两个进行运算的浮点数的阶码(小数点的位数是否对齐)对齐的操作。对阶的目的是为了使两个浮点数的尾数能够进行加减运算。

由于指数位数不同,运算时需要对阶运算,这个过程也有可能会损失精度。

0.1+0.2的二进制对阶之后计算得到的二进制:
0.0100110011001100110011001100110011001100110011001100 

转化成十进制:0.30000000000000004

如何解决0.1+0.2不等于0.3的问题

1.设置一个误差范围值
设置一个误差范围值,通常称为“机器精度”,对于JavaScript来说,这个误差范围是Math.pow(2,-52)
在ES6中,提供了一个属性,Number.EPSILON,这个值等于2的﹣52次方

Number.EPSILON === Math.pow(2,-52)  // true

所以,只要判断(0.1+0.2)与0.3的差小于Number.EPSILONG,就可以说明两者是相等的

/**
 * @description 比较两个值是否相等
 * @param {Number} a 
 * @param {Number} b 
 * @return 相差小于某个值,返回true,否则返回false
 */
function numberEqual(a,b){
  return Math.abs(a-b) < Number.EPSILON
}

numberEqual((0.1+0.2), 0.3) // true

参考

https://juejin.im/post/5b90e00e6fb9a05cf9080dff

你可能感兴趣的:(JavaScript中0.1+0.2==0.3返回false)