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个二进制位,从最左边开始,是这样组成的:

  • 第一部分(蓝色):用来存储符号位(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 类型。

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值