su37josephxia/frontend-interview

Day1 - JS 整数是怎么表示的?

su37josephxia opened this issue · 19 comments

知识讲解

系统 + 全面

万能的Number

JavaScript 内部,所有数字都是以64位浮点数形式储存,即使整数也是如此。所以,1与1.0是相同的,是同一个数。

做一个实验

typeof 1 // number
typeof 1.0 // number
1 === 1.0

Number类型的表示范围

浮点数的结构

根据国际标准 IEEE 754,JavaScript 浮点数的64个二进制位。

image-20220101175727812

  • 第一部分(蓝色):用来存储符号位(sign),第1位:符号位,0表示正数,1表示负数
  • 第二部分(绿色):用来存储指数(exponent),第2位到第12位(共11位):指数部分
  • 第三部分(红色):用来存储小数(fraction),第13位到第64位(共52位):小数部分(即有效数字)

fraction决定了整数安全表示范围

也就是

(-1)^s * f * 2^e

  • 符号部分 -1 or 1

  • f的范围为1<=f<2 使用52位表示

  • 指数有正有负,指数位长度为11比特,所以能表示的数字范围为0~2047

所以很明显最大位应该由f决定,也就是说所有的52位用作表示整数部分。

52位为什么可以表示53位小数

因为小数部分只需要表示尾数就可以,整数部分可定等于一

52位太多不好理解,假设我们以3位(bit)数

0.10 (二进制) 可以表示为 1.00 * 2^-1

0.01 (二进制) 可以表示为 1.00 * 2^-2

这样的话由于整数部分一定等于1,所以可以把整数部分省略。

也就是说3位数可以表示做小数表示的时候可以表示4位小数

为什么不是指数部分决定的

0.1123 * 2^1024

当然这里面有人会问为什么不是指数部分决定呢。上面这个数的范围是不是比我们的讨论的数据范围更大呢。

其实并不是这样,因为实用指数表示并不能表示连续的数字。所以这个方案不可取。

整数的表示范围

Math.pow(2,53)  - 1 // 最大 
Number.MAX_SAFE_INTEGER // 常数表示
- (Math.pow(2,53)  - 1) // 最大 
Number.MIN_SAFE_INTEGER // 常数表示

面试攻略

精炼 + 系统 + 自洽

  • 基本概念:JavaScript 内部,所有数字都是以64位浮点数形式储存。
  • 整数表示范围不用直接分析

点评

  • 这道题属于基本概念题,但是如何用二进制表示浮点数很容易能够看出来,逻辑非常复杂。很容易看出应聘这的基本功,和理性思维能力。
    • 初级: 知道Number
    • 中级: 说出表示范围推导过程
    • 高级: 推导过程分析合理且可以和其他语言对比。

有了第一题的回答,大家都明白 IEEE 754 中规定有 11位的指数位,52位的小数位。
所以如何让这52位小数位变成整数是关键。
我们知道在10进制中如果想把n位小数变成整数就乘 10^n, 所以在二进制也是一样,当11位的指数位表示的数值为52时,我们就可以表示成整数了。
可能有些小伙伴会问,如果是这样为啥最大的安全整数不是2^52 -1 吗,为什么是2^53-1呢?
因为在科学计数法的尾数部分是大于1的,我们肯定不能以0为尾数部分的开头,所以这个尾部的1是被省略掉的。我们的52位是小数位,所以最大的安全整数时2^53-1而不是2^52-1.
但是还有一个问题没有被解决,就是既然科学计数法中的尾部1是被默认设置了的。我们就没有指数位为52是我们没办法表示0.
关于0,IEEE 754有专门的规定,规定指数位为0,小数位为0,则表示为0.而JS表示的浮点数有sign位,所以这就解释了JS中有两种0的表示方式。
以上就是关于JS整数是如何表示的简单解释。

PS:我昨天向群里的小伙伴安利了我之前写的文章,由于当时没玩掘金,所以只是写完后复制到掘金上,今天发现有很多排版问题,同时也有些知识点不明确和错误的地方,我又进行了重新排版,感兴趣的可以去看看,欢迎留言讨论https://juejin.cn/post/6844903581993140237

在JavaScript中数字为双精度浮点类型遵循 IEEE754 标准
介于±2^−1023和±2^+1024之间的数字,或约为±10−308到±10+308,数字精度为53位。整数数值仅在±(2^53 - 1)的范围内可以表示准确

默认十进制数,数字添加0b表示二进制数,0开头且所有数字小于8表示表示八进制数;0x开始表示16进制数

这就是我理解的JavaScript整数表示方法

扩展

什么是双精度浮点型?
浮点型可以理解为【科学计数法】,双精度是相对于单精度,单精度完全保证的有效数字最高位是7位,双精度完全保证的有效数字最高是15位;有啥区别呢?双精度提供了更高的数值精度;

参考

https://javascript.ruanyifeng.com/grammar/number.html#toc1

答案

(考察 JS 怎么表示数字)。

在 JavaScript 内部,所有数字都是以 64 位浮点数存储,从左边开始:
1 位符号位:0 正数,1 负数,
11 位表示指数(0 到 2047),
52 位表示小数(有效数字)。

由于有效数字第 1 位总是 1,不保存在浮点数里,所以 JS 提供的有效数字最长为 53 个二进制位。
因此只有绝对值小于 2 的 53 次方 (Math.pow(2, 53) - 1)的数字才能精确表示。

JS整数是使用Number类型来表示的,Number类型使用IEEE 754格式表示整数和浮点数
整数一般用十进制表示,也可以用八进制或十六进制字面量表示
对于八进制字面量,第一个数字必须是0,然后是相应的八进制数字
要创建十六进制字面量,必须让真正的数值前缀 0x(区分大小写),然后是十六进制数字

JS遵循 IEEE 754规范,采用双精度存储,占用64bit
1 位用来表示符号位
11 位用来表示指数
52 位表示尾数
尾数位最大是 52 位,因此 js 中能精准表示的最大整数是 Math.pow(2, 53)

一般来说64位整形数字的最大安全值范围是2^63-1,JS中的数字类型,实际上是通过8字节double浮点型表示的,浮点数并不能够精确表示范围内的所有数字,正常的浮点数类型遵循IEEE754规范,按照浮点数的运行规范,1位符号位,11位指数位,剩下的是52位数字位,因此js中的数字类型的表示最大安全范围是2^53 - 1 位数。

可以使用 Number.MAX_SAFE_INTEGER来表示 即
Math.pow(2, 53) - 1

JS如何表示整数

首先在js中使用number数据类型表示整数 (64位存储 8 字节)


js number类型是遵循IEEE754标准的64位双精度浮点数。
标准中指明1位符号位11位指数位52位尾数, 取值范围取决于指数位,计算精度取决于尾数
所以最大数为2 ^ 1024 - 1(2^11 = 2048, +-指数/2 = 1024), 最大安全数为2^53-1

  • 那么这里为什么需要减一而且不是52呢?
  • IEEE754规定,在计算机内部保存有效数字时,默认第一位总是1, 所以舍去,只保留后面的部分。比如保存1.01,只存01,等读取的时候再把第一位1加上去。所以52位有效数字实际可以存储53位。
  • 那么为什么超过最大安全数计算就不再准确了?
  • 在安全数范围中,双精度数和整数一一对应, 也就是说, 整数都有唯一的浮点数表示, 但是象2^53+1和2^53存储一样,这样就不再一一对应。
  • 为什么超过最大安全数的两个数会存储的一样呢?
  • 因为在指数位是53的情况下,尾数在乘以2的53次幂后, 相当于小数点向后移动了53位。此时, 小数点不是在尾数最后一位的后面, 而是在其后面的后面, 所以它们存储的一样

js的底层根本没有整数,都是用二进制64位浮点数表示
根据国际标准IEEE754,64位二进制从左开始

  • 第1位:符号位,0正数,1负数
  • 第2位到12位(11位):指数部分,0-2047,正负各一半,因此数值范围在2^-1023~2^1024
  • 第13位到第64位(52位):小数部分,控制精度,有效数字第一位总是1,只保留后面的小数位,因此有53个二进制位有效数,-2^53~2^53范围内的数都可以精确表示

JS整数是怎么表示的

通过Number类型表示

JS Number 类型是双精度的IEEE754 64位浮点类型

1位是符号位

11位是指数位

52位是尾数位

IEEE754规定有效数字第一位总是1,也即是说有效数字总是1.xxx的形式.xxxx是52位尾数位

所以js中有效的数字是52个二进制位 所以我们的安全数值田-Math.pow(2, 53) - 1 到 Math.pow(2, 53) - 1

在我们代码中表示整数方式:

  • 字面量 常见的十进制,
  • 0开始的8进制(0-7)
  • 0x开始的16进制(0-9A-F)
  • 2进制 0b开头 ,

但转化或运算后都已十进制显示

表达方式

  • 还有可以通过new Number的形式
  • 还有一些 parseInt(), Math.floor()的等等一些方法

一些较大的值或转成科学计数法的方式:e前面的数值乘以10的指数幂

在JS里我们用 Number 来表示整数,Number,Double Float,浮点数是采用科学计数法来表示的,由符号位、有效位数、指数位三部分组成

有效位数决定了这个数的精度,指数位表示浮点数表示的范围,这里的位是指二进制,0或1表示,浮点数可以表示很大的数,在表示最大数的时候,可能不是每个整数都能表示了,数越大能表示的数就越稀疏

双精度浮点数是64位组成:
符号位1位:不参与计算的,0表示正数1表示负数
指数位11位:有个基准值,10000000000
有效位数52位:有效数字隐藏了个1,因为以0开头没有意义
数字 = 符号位 + 精度位*2^指数次方

在 JS中,所有数字都是以 双精度64 位浮点数存储,
其中包括1位符号位,11位指数位,52位小数位,
用来控制精度的是52位小数位,浮点数在内存中是按科学计数法来存储的,其整数部分始终是一个隐含着的1
所以JS中可以明确表示精度的数字范围在正负2的53次方,而不是52次方

JavaScript 中使用 Number 类型表示整数,而且遵循 IEEE 754 格式。

Number 类型

JavaScriptNumber 类型使用 IEEE 754 格式表示整数和浮点值。

IEEE 754 中双精度浮点数使用 64 bit 来进行存储:
第一位存储符号表示正负号 0 正 1 负
2-12位存储指数表示次方数
13-64位存储尾数表示精确度

浮点数在计算机中的存储方式:
浮点数会被转成对应的二进制数,并用科学计数法表示,
然后把这个数值通过 IEEE 754 标准表示成真正会在计算机中存储的值。

扩展

因为内存的限制,Number 类型并不能表示这个世界上的所有数值,当然也包括所有整数。
JavaScript 中所能表示的最大安全整数 Math.pow(2, 53)-1,最小安全整数为 -Math.pow(2, 53)-1
为什么最大安全整数是 Math.pow(2, 53)-1 呢?

  1. 首先解释位数的问题,因为 IEEE 754 规定尾数第一位隐含为1,不写,所以虽然尾数有52位,但是可以表达53位数。
  2. 关于安全,安全 的意思是说能够one-by-one表示的整数,也就是说在(-2^53, 2^53)范围内,双精度数表示和整数是一对一的,反过来说,在这个范围以内,所有的整数都有唯一的浮点数表示,这叫做安全整数。

那为什么要 -1 呢?
是因为安全的意思是整数和浮点数一一对应,而
2^53-1是这样存的:
符号位:0 指数:52 尾数:1.111111....111 (小数点后一共52个1)
2^53是这样存的:
符号位:0 指数:53 尾数:1.000000...000 (小数点后一共52个0)
2^53+1是这样存的:
符号位:0 指数:53 尾数:1.000000...000 (小数点后一共52个0)
可以看到因为 2^53 已经是 64 bit 双精度浮点数可以表示的最大值了, 2^53 与 2^53+1 转成 64 bit 双精度浮点数后的结果是相同的,此时发生了精度丢失,所以 2^53 不是安全整数。

JavaScript 中如果数值超出了可以表示的范围会怎样呢?

如果计算得到的值超出了 JavaScript 可以表示的范围,那么这个数值会被自动转换为一个特殊的 Infinity(无穷)值。任何无法表示的负数以 -Infinity (负无穷大)表示,任何无法表示的正数以 Infinity(正无穷大)表示。

要确定一个值是不是有限大(即介于 JavaScript 能表示的最小值和最大值之间),可以使用 isFinite() 函数,如下所示:

const result = Number.MAX_VALUE+Number.MAX_VALUE;
console.log(isFinite(result); // false

js中整数用Number类型表示,然后存为双精度浮点数,1位符号位表示正负,11位指数位表示范围,52位有效数字表示精度。因为有效数字只有52位,所以超过了52位就会表示不准确。然后就出现了安全整数的概念,就是在-2的53次方到2的53次方之间,每一个整数都有唯一一个浮点数和它一一对应。在这个区间之外则无法保证。也就无法保证准确性了。之所以是53次方是因为根据规范默认有效数字开头是1,然后把腾出了一个空间多记了一个数,所以是2的53次方。

+0 和 -0 是因为符号位占了一位,所以其他为0时就出现了+0和-0。

在 Javascript 中数字只有一种类型,即双精度,64 位二进制,符合 IEEE754 格式的值。
所有的数字都是用 64 位来表示,
其中 1 位是符号位,11 位是指数位,52 位表示小数位就是有效的数字
因为有效数字的默认第 1 位终是 1 那么就不用浪费 1 位去保存它了,只需要在执行数学运算的时候,把这个 1 补回来即可;
所以 js 提供的有效数字位是 53 个二进制位,
最后绝对值小于 2 的 53 次方的整数就能被精准的表示。

  • js中的Number 类型按 IEEE 754(浮点数运算标准) 来表示整 数和浮点值(双精度值)。

  • 双精度二进制小数,使用64个比特位存储。

  • 1位,S(符号位),0表示正,1表示负;

  • 11位,E(指数)

  • 52位,M(小数位),任意有效数字

  • JS 中能精准表示的最大整数是 Math.pow(2, 53),十进制即 9007199254740992。

  • 双精度浮点数有效数字16位

  • 最基本的数值字面量格式是十进制整数,除了十进制,整数也可以用八进制(以 8 为基数)或十六进制(以 16 为基数)字面量表示。

  • 使用八进制(前缀0o)和十六进制(前缀0x)格式创建的数值在所有数学操作中都被视为十进制数值

  • 因为存储浮点值使用的内存空间是存储整数值的两倍,所以 ECMAScript 总是想方设法把值转换为 整数。在小数点后面没有数字的情况下,数值就会变成整数。

  • js中的Number 类型按 IEEE 754(浮点数运算标准) 来表示整 数和浮点值(双精度值)。

  • 双精度二进制小数,使用64个比特位存储。

  • 1位,S(符号位),0表示正,1表示负;

  • 11位,E(指数)

  • 52位,M(小数位),任意有效数字

  • JS 中能精准表示的最大整数是 Math.pow(2, 53),十进制即 9007199254740992。

  • 双精度浮点数有效数字16位

  • 最基本的数值字面量格式是十进制整数,除了十进制,整数也可以用八进制(以 8 为基数)或十六进制(以 16 为基数)字面量表示。

  • 使用八进制(前缀0o)和十六进制(前缀0x)格式创建的数值在所有数学操作中都被视为十进制数值

  • 因为存储浮点值使用的内存空间是存储整数值的两倍,所以 ECMAScript 总是想方设法把值转换为 整数。在小数点后面没有数字的情况下,数值就会变成整数。

在js中 通过 Number 类型来表示整数,以双精浮点数的方式在计算机中储存和运算,遵循 IEEE754 标准,它通过 64 位来表示一个数字。

  • 1位,S(符号位),0表示正,1表示负;
  • 11位,E(指数)数字的指数
  • 52位,M(小数位),任意有效数字
    因为有效数字的默认第 1 位终是 1 那么就不用浪费 1 位去保存它了,所以实际有效数字位是 53 位

所以,js十进制表示的最大安全数字是 Math.pow(2, 53) - 1 。

首先,得知道,js是使用IEEE 754格式的双精度浮点数。1bit表示符号位,11bit表示指数位,52bit表示尾数位。

  • 符号位,0表示整数,1表示负数。
  • 指数位,由于有11位,可以表示0~2^11-1的指数即0~2047,由于指数有正负,偏移一个1024,即得-1024~1023,这里规定去掉0和2047两个边界值不予使用,那么少两个数,偏移量也少1,即表示的指数范围为-1023~1022。
  • 尾数位,由于科学计数法都为1开头,那么1可以默认不存储,则52bit其实可以表示长度为53位的二进制科学计数的系数。

讲到这里,js可表示最大数,应为1.1111---111*2^1022(小数点后52个1),是一个很大的数,但是尾数精度实际只有52,超出的部分都丢失掉了,所以最大的整数应该是1.111---111*2^52(小数点后52个1),这样可以保证数的每一位都有记录下来,答案就是2^53-1。

JS中的Number类型其实就是双精度浮点数,它是按IEEE754标准实现的,在这个标准中是用科学计数法来表示一个数的。

那么具体而言的话,双精度浮点数,它其中的一位是用来表示这个数正负的,11位是用来表示科学计数法中的指数,用52位来表示科学计数法中的有效数字。

因此的话,在JS中安全的最大整数就是Math.pow(2, 53)-1,这是因为有效数字有效数位是53位。

Ps: 最小安全整数为 -Math.pow(2, 53)-1