Loading... # [JavaScript的64位浮点数](https://blog.csdn.net/m0_46655912/article/details/122871297) ## 一、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](http://flt-pan.58heshihu.com/blog/typecho/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 类型。 最后修改:2023 年 12 月 07 日 © 允许规范转载 赞 如果觉得我的文章对你有用,请随意赞赏