JavaScript的64位浮点数

dafenqi
2023-12-07 / 0 评论 / 8 阅读 / 正在检测是否收录...

JavaScript的64位浮点数

一、JavaScript的64位浮点数

 JavaScript 内部,所有数字都是以64位浮点数形式储存,即使整数也是如此。

 所以,1与1.0是相同的,是同一个数。

1 === 1.0 // true

这就是说,JavaScript 语言的底层根本没有整数,所有数字都是小数(64位浮点数)。容易造成混淆的是,某些运算只有整数才能完成,此时 JavaScript 会自动把64位浮点数,转成32位整数,然后再进行运算。

由于浮点数不是精确的值,所以涉及小数的比较和运算要特别小心。

0.1 + 0.2 === 0.3

// false

0.3 / 0.1

// 2.9999999999999996

二、IEEE754 浮点数标准的制定背景 

早期人们提出浮点数定义时,每个计算机厂商会定义自己的浮点数规则,不同厂商对同一个数表示出的浮点数是不一样的。

这就会导致,一个程序在不同厂商下的计算机中做浮点数运算时,需要先转换成这个厂商规定的浮点数格式,才能再计算,这也必然加重了计算的成本。

于是,1985年,IEEE 组织推出了浮点数标准,就是我们经常听到的 IEEE754 浮点数标准,这个标准统一了浮点数的表示形式,并提供了 2 种浮点格式:

单精度浮点数 float:32 位,符号位 s 占 1 bit,指数 e 占 8 bit,小数数 f 占 23 bit

双精度浮点数 float:64 位,符号位 s 占 1 bit,指数 e 占 11 bit,小数 f 占 52 bit

三、JavaScript浮点数在内存中的结构

根据国际标准IEEE 754 ,JavaScript 浮点数的64个二进制位,从最左边开始,是这样组成的:

lpv056yj.png

第一部分(蓝色):用来存储符号位(sign),第1位:符号位,0表示正数,1表示负数

第二部分(绿色):用来存储指数(exponent),第2位到第12位(共11位):指数部分

第三部分(红色):用来存储小数(fraction),第13位到第64位(共52位):小数部分(即有效数字) 

符号位决定了一个数的正负,指数部分决定了数值的大小,小数部分决定了数值的精度。

四、 64位浮点数表示数字的公式

浮点数是采用科学计数法来表示一个数字的,它的格式可以写成这样:

(-1)^s f 2^e

符号部分 -1 or 1

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

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

假设我们将十进制数 25.125 转换为浮点数,转换过程就是这样的(D代表十进制,B代表二进制):

整数部分:25(D) = 11001(B)

小数部分:0.125(D) = 0.001(B)

用二进制科学计数法表示:25.125(D) = 11001.001(B) = 1.1001001 * 2^4(B)

所以符号位 s = 0,小数 f = 1.001001(B) = 001001(去掉1,隐藏位),指数 e = 4+1023(中间值) = 1027(D) = 10000000011(B)。

按照内存结构中浮点数定义的规则,填充到 64 bit 上。

五 、52位为什么可以表示53位小数(精度)

IEEE754规定小数部分第一位隐含为1,不写,因为所有二进制第一个有效数字都是1。

所以加上省略的1位,精度位数是 53 bit。所以在 0 ~ 2^53 内的整数都是有效数字,算上第1位符号位,就可以得到 -2^53 ~ 2^53 都是有效数字。

六、浮点数能精确表示的范围

Math.pow(2,53)  - 1 // 最大 

Number.MAX_SAFE_INTEGER // 常数表示

- (Math.pow(2,53)  - 1) // 最大 

Number.MIN_SAFE_INTEGER // 常数表示

七、Number的MAX_VALUE

我们知道了 js 中数的表示方法,那么他能表示的最大的数是多少呢,聪明的你肯定会想到是下面这个数:

0 11111111111 1111111111111111111111111111111111111111111111111111

但是,这种情况在 IEEE754 标准中表示 NaN,最大的数其实是:

0 11111111110 1111111111111111111111111111111111111111111111111111

转换成二进制的科学计数法表示如下:

1.1111111111111111111111111111111111111111111111111111 * 2^(2046 - 1023)

= 1.1111111111111111111111111111111111111111111111111111 * 2^1023

= 11111111111111111111111111111111111111111111111111111 * 2^971

= (2^53 - 1) * 2^971

= 1.7976931348623157e+308

我们在浏览器调试窗口里面验证下:

(Math.pow(2, 53) - 1) * Math.pow(2, 971) // 1.7976931348623157e+308

(Math.pow(2, 53) - 1) * Math.pow(2, 971) === Number.MAX_VALUE // true

八、Number的MAX_SAFE_INTEGER

MAX_SAFE_INTEGER 表示在 JavaScript 中最大的安全整数。所谓的安全,就是大于这个数的整数不一定可以精确表示。他的值其实是 2^53 - 1,表示成二进制为:

0 10000110100 1111111111111111111111111111111111111111111111111111

表示成二进制的科学计数法为:

1.1111111111111111111111111111111111111111111111111111 * 2^52

= 11111111111111111111111111111111111111111111111111111

比这个数大一的数为:

100000000000000000000000000000000000000000000000000000

= 1.00000000000000000000000000000000000000000000000000000 * 2^53

在计算机中表示成:

0 10000110101 0000000000000000000000000000000000000000000000000000 0

注意到我们省去掉了一位,按照向偶舍入的规则,不会产生进位。所以这个数还是可以精确表示的,没有问题。

我们再来看看比 MAX_SAFE_INTEGER 大二的数:

100000000000000000000000000000000000000000000000000001

= 1.00000000000000000000000000000000000000000000000000001 * 2^53

在计算机中表示成:

0 10000110101 0000000000000000000000000000000000000000000000000000 1

注意到我们省去掉了一位,按照向偶舍入的规则,还是不会产生进位。这个时候就有问题了,这个数跟刚才那个数竟然是相等的,我们来验证下:

const a = Number.MAX_SAFE_INTEGER

a + 1 === a + 2 // true

所以,在进行大数的相关运算的时候要小心了,最好是使用 BigInt 类型。

0

Deprecated: strtolower(): Passing null to parameter #1 ($string) of type string is deprecated in /www/wwwroot/testblog.58heshihu.com/var/Widget/Archive.php on line 1032

评论 (0)

取消