JavaScript的64位浮点数

本文详细介绍了JavaScript如何以64位浮点数形式存储数字,以及IEEE754浮点数标准的背景和应用。讨论了JavaScript中浮点数的精度问题,最大安全整数,以及浮点数表示范围。还涵盖了浮点数运算可能导致的不精确性和比较问题,提醒开发者在处理大数时注意使用BigInt类型。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、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 类型。

在选择支持64浮点数运算的库时,需要考虑库的精度控制能力、性能表现以及是否易于集成到现有项目中。以下是几个推荐的64浮点数运算库,适用于不同编程语言和应用场景。 ### 1. SoftFloat(C/C++) SoftFloat 是一个广泛使用的高精度浮点数运算库,支持3264以及扩展精度的浮点数运算。它遵循 IEEE 754 标准,适合需要高精度浮点运算的场景。SoftFloat 提供了完整的 API 接口,可以用于实现加法、减法、乘法、除法等基本运算,同时支持自定义舍入模式和异常处理[^1]。 ```c #include <softfloat.h> int main() { float64_t a = float64_fromRaw(0x3FF0000000000000); // 1.0 float64_t b = float64_fromRaw(0x3FF0000000000000); // 1.0 float64_t result = float64_add(a, b); printf("Result: %lf\n", result); return 0; } ``` ### 2. MPFR(C/C++) MPFR(Multiple-Precision Floating-Point Reliable)是一个用于多精度浮点运算的库,支持任意精度的浮点数运算。它基于 GMP(GNU Multiple Precision Arithmetic Library) 构建,提供了比 SoftFloat 更高的灵活性和精度控制能力。MPFR 适用于需要极高精度的科学计算、数值分析等领域[^3]。 ```c #include <mpfr.h> int main() { mpfr_t a, b, result; mpfr_init2(a, 64); // 初始化为64精度 mpfr_init2(b, 64); mpfr_init2(result, 64); mpfr_set_d(a, 1.0, MPFR_RNDN); mpfr_set_d(b, 1.0, MPFR_RNDN); mpfr_add(result, a, b, MPFR_RNDN); // 加法运算 mpfr_out_str(stdout, 10, 0, result); // 输出结果 printf("\n"); mpfr_clear(a); mpfr_clear(b); mpfr_clear(result); return 0; } ``` ### 3. BigDecimal(Java) 对于 Java 开发者,`BigDecimal` 是标准库中用于高精度浮点数运算的类。它支持任意精度的十进制浮点数运算,特别适合金融计算等对精度要求极高的场景。`BigDecimal` 可以避免由于二进制浮点数表示导致的精度丢失问题[^4]。 ```java import java.math.BigDecimal; public class Main { public static void main(String[] args) { BigDecimal a = new BigDecimal("0.1"); BigDecimal b = new BigDecimal("0.2"); BigDecimal result = a.add(b); System.out.println("Result: " + result); // 输出 0.3 } } ``` ### 4. Decimal.js(JavaScript) 在前端或 Node.js 环境中,`decimal.js` 是一个轻量级的高精度浮点数运算库,支持64甚至更高精度的浮点数运算。它解决了 JavaScript 原生浮点数运算中常见的精度问题,如 `0.1 + 0.2 !== 0.3` 的问题[^5]。 ```javascript const Decimal = require('decimal.js'); let a = new Decimal(0.1); let b = new Decimal(0.2); let result = a.plus(b); console.log(result.toString()); // 输出 "0.3" ``` ### 5. NumPy(Python) 对于 Python 开发者,`NumPy` 是一个广泛使用的数值计算库,支持高效的64浮点数数组运算。虽然它不提供任意精度的计算,但在科学计算、机器学习等领域中,`NumPy` 提供了出色的性能和易用性[^3]。 ```python import numpy as np a = np.float64(1.0) b = np.float64(1.0) result = a + b print("Result:", result) # 输出 2.0 ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值