先记住这个答案
JavaScript 的 Number 类型遵循 IEEE 754 双精度浮点标准,有效精度是 53 位二进制。所以安全整数的范围是从 -(2^53 - 1) 到 2^53 - 1,对应 Number.MIN_SAFE_INTEGER 和 Number.MAX_SAFE_INTEGER,也就是正负 9007199254740991。超过这个范围,并不是所有整数都能精确表示了——相邻整数可能被舍入成同一个值,导致加法、比较等运算出错。需要精确大整数时,应该用 BigInt。
- 最大安全整数是
2^53 - 1 - 超出后相邻整数可能相等
BigInt提供任意精度整数- 用
Number.isSafeInteger检查整数
为什么边界是 2 的 53 次方减 1
双精度浮点由 1 位符号、11 位指数和 52 位尾数组成。规格化的浮点数在尾数前面隐含了一个 1,所以实际有效位数是 53 位。53 位能表示的最大整数就是 2^53 - 1,也就是 9007199254740991。
当数值增长到 2^53 时,需要 54 位才能精确表示每一个整数,但尾数只有 53 位,所以步长变成了 2——引擎只能表示偶数。2^53 本身能表示(因为它正好是 2 的幂),但 2^53 + 1 就无法表示了,会被舍入成 2^53。
MAX_SAFE_INTEGER 溢出示例JavaScriptconsole.log(Number.MAX_SAFE_INTEGER);
var a = Number.MAX_SAFE_INTEGER + 1;
var b = Number.MAX_SAFE_INTEGER + 2;
console.log(a);
console.log(b);
console.log(a === b);
console.log(Number.isSafeInteger(a));查看输出与解释
9007199254740991
9007199254740992
9007199254740992
true
falsea 和 b 本该差 1,但 +2 的结果被舍入成了和 +1 一样的值。Number.isSafeInteger() 返回 false,说明这个数已经不在安全范围内了。
实际项目里什么时候会踩到这个坑
最常见的场景是后端返回的 ID 超过了安全整数范围。比如雪花算法生成的订单号是 64 位长整型,很多已经超过 9007199254740991。前端用 JSON.parse 解析响应时,JavaScript 会把 JSON 里的数字字面量直接存成 Number,这时候精度就丢了。两个不同的订单号可能变成同一个数字,用户点开的可能不是自己那个订单。
解决办法是让后端把大 ID 序列化成字符串,前端全程当字符串处理。如果确实需要做数值运算,就用 BigInt。BigInt 可以表示任意大小的整数,不存在精度丢失的问题。
BigInt 做精确大整数运算JavaScriptvar big = BigInt('9007199254740993');
console.log(big + 1n);
console.log(typeof big);查看输出与解释
9007199254740994
bigintBigInt 通过在数字后面加 n 或用 BigInt() 构造,运算时也要用 BigInt 字面量(1n),不能和普通 Number 直接混用。big + 1 会抛 TypeError,必须写成 big + 1n。
容易答错的地方
- 以为超过
MAX_SAFE_INTEGER就完全无法表示 - 超出安全范围的数仍然存在,
Number.MAX_VALUE大约是1.8e308,远大于MAX_SAFE_INTEGER。只是整数的步长变大了,不是每个整数都能唯一表示。打个比方,安全范围内你能精确到每一厘米,超出后只能精确到每两厘米甚至更粗。 - 混淆
MAX_VALUE和MAX_SAFE_INTEGER Number.MAX_VALUE是最大的有限浮点数,约1.8e308。Number.MAX_SAFE_INTEGER是2^53 - 1,约9e15。两者差了几百个数量级。面试中把它们搞混会暴露概念不清晰。
面试官还会怎么问?
为什么安全边界是 2^53 - 1 而不是 2^53?
2^53 本身可以精确表示,但 2^53 + 1 无法表示,会被舍入成 2^53。安全整数要求这个数和它相邻的整数都能精确表示,2^53 的下一个整数 2^53 + 1 已经丢了,所以 2^53 不算安全整数,边界取到 2^53 - 1。
BigInt 和 Number 混合运算会怎样?
直接做加减乘除会抛 TypeError,因为它们是不同的原始类型。但关系比较是允许的:1n < 2 返回 true,1n === 1 返回 false(类型不同),1n == 1 返回 true(宽松相等做了转换)。
怎么快速判断一个数是不是安全整数?
用 Number.isSafeInteger(),它内部的逻辑等价于:先检查是不是整数(Number.isInteger()),再检查绝对值是否小于等于 Number.MAX_SAFE_INTEGER。传入非 number 类型直接返回 false。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。