由一个相等操作说开来去

164 阅读3分钟

序言

事情的起因是这样子的,公司微信群里,有个同事问了这样子的一个问题

9986705337161735 == 9986705337161736 // true

为什么表面上看起来不相等的两个数字却会相等呢?为什么我说是表面相等呢。。。接下来细说。 话说在这个同事提出这个问题后,马上有个同学说了应该是数字过大,JS都当成Infinity了吧!后来查了下JS最大支持的数字是1.7976931348623157e+308显然比我们这个数字大的多了。

那么事情的真相是什么呢?

假设一:等号操作前会对左右表达式进行隐式转换

针对假设一我们想象下隐式转换的规则,我们知道对于非数值得会转换为数值进行比较,但是现在两边均为数值是如何进行隐式转换呢?

假设二:隐式转换规则为Number(要比较的值)

  Number(9986705337161735) // 9986705337161736
  Number(9986705337161736) // 9986705337161736

果然经过Number转换后确实左右相等了。

那么又有一个问题了为什么Number(9986705337161735)会得到9986705337161736呢? 翻看MDN中关于Number这一节,可以知道JS中有一个最大安全数(JavaScript 中最大的安全整数 (2的53次方 - 1))也就是9007199254740991。最大安全数有什么意义呢?就是大于最大安全数的数字都是不准的。类比于最大安全数,还有一个最小安全数。可以看到我们用来比较的两个数都远远地大于最大安全数。所以他们是不安全的,也就是说他们的实际值是有偏差的。👌,这就完美的解释了为什么Number()过后数字会变成9986705337161736.

现在我们来看看假设一,那么JS中存不存在对数值类型进行隐式转换的规则呢?我们看看ECMA规范中有没有明确!

在规范的

The conversion of a String to a Number value is similar overall to the determination of the Number value for a numeric literal (see 11.8.3), but some of the details are different, so the process for converting a String numeric literal to a value of Number type is given here.

主要是这一句将字符类型转换为数值类型和将数值文本转换为数值值得规则相同,我们知道JS 内部有将数值文本转换为数值值得操作,那么这个规则的说明在哪呢?继续看

A numeric literal stands for a value of the Number type. This value is determined in two steps: first, a mathematical value (MV) is derived from the literal; second, this mathematical value is rounded as described below.

我们可以看到确实有转换规则,首先由数字文本产生MV(数学上的值),然后对产生的数学值进行四舍五入。

所以现在我们大致知道为什么这个表达式会有这样子的表现了。