likes
comments
collection
share

Math.pow(2, 53) 等于 Math.pow(2, 53) + 1 ?

作者站长头像
站长
· 阅读数 19

要想搞明白为什么 Math.pow(2, 53) 等于 Math.pow(2, 53)+1?我们需要从 javascript 底层如何表示数字入手,内容很干,请耐心看完。

看完这篇文章你将收获:

  1. javascript 数字在底层是如何表示的?

  2. Math.pow(2, 53) 为什么等于 Math.pow(2, 53) + 1 ?

  3. 为什么最大安全整数是 2^53 - 1?

  4. NaN 和 Infinity 到底是什么?

数字编码

首先看一下 javascript 底层是如何表示数字的。javascript 的数字是基于 IEEE754 标准实现的,用 64 位二进制表示,结构如下:

Math.pow(2, 53) 等于 Math.pow(2, 53) + 1 ?

  • 1 位符号位,表示正数或负数;

  • 11 位用来表示指数变量 e(0 < e < 2047),不包括最大值和最小值,因为当 e 为 0 和 2047 时有特殊用途,下文会提到;

  • 52 位用来表示小数 (表示一个介于 0 到 1 的数字),因为整数部分固定是 1,所以不用表示出来;

用公式表示:

Math.pow(2, 53) 等于 Math.pow(2, 53) + 1 ?

来看几个示例,加深理解:(f 表示小数部分,p 表示实际的指数)

Math.pow(2, 53) 等于 Math.pow(2, 53) + 1 ?

对于整数来说,可以这样理解:p 表达的含义是 f 的 p 个 bit 位是有效的。 比如示例4,f 的 4 个 bit 位是有效的。下图展示的是当 p 取不同值时与 f 的关系。

Math.pow(2, 53) 等于 Math.pow(2, 53) + 1 ?

最大安全整数

按照上面的逻辑会有个问题,f 最多有 52 位,上面提到 p 的最大值是 1023,所以 p 的取值可能远远大于 f 的位数。那如果 p 大于 52 会怎样?直接来看图,当 p 大于 52 时,在 f0 右侧会补上缺少的 bit 位。

Math.pow(2, 53) 等于 Math.pow(2, 53) + 1 ?

当这些补位变化时,其实并不会影响数值的变化,因为补位变化既不会影响 f 也不会影响 p。如果这句话如果难理解,可以先继续往下看。

为什么 Math.pow(2, 53) 等于 Math.pow(2, 53) + 1? 因为后者 +1 之后,f 并没有变化。

Math.pow(2, 53) 等于 Math.pow(2, 53) + 1 ?

这就是安全整数(MAX_SAFE_INTEGER)的由来,因为当数字大于 2^53-1 时,会出现不同整数在 javascript 里却是同一个整数的现象,所以 javascript 中最大安全整数是 2^53-1。

NaN 与 Infinity

在前面我们提到 e 的最小值 0 和最大值 2047 有特殊用途,有什么用途呢?我们一起来看下,当 e = 2047 时,用来表示 Infinity 和 NaN(通过 f 来区分);当 e 等于 0 时有两个能力:

  1. 如果 f 也是 0,那么表示的数字就是 0,由于有符号位,所以会出现 +0 和 -0;

  2. 如果 f > 0,可以用来表示一个很小的数,接近于 0(0.f * 2 ^ (-1022)),这种表示称为非规范化的(之前介绍的都是规范化的);

Math.pow(2, 53) 等于 Math.pow(2, 53) + 1 ?

总结

到这儿主要内容就讲完了,通过这篇文章希望大家能够对 javascript 的最大安全数有更深刻的了解,如果有些地方我没讲明白,欢迎留言交流。

参考

2ality.com/2012/04/num…

en.wikipedia.org/wiki/Double…