← 返回题目列表

JavaScript 数值精度、NaN 和安全整数怎么理解?

中等 第 22 / 29 题 更新于 2026/07/29
JavaScriptNumber精度

简化版

JavaScript 的普通数字使用 IEEE 754 双精度浮点数表示,所以整数、小数都走同一种 Number。这会带来小数精度误差、安全整数上限、NaN 不等于自身等问题。

常见判断:用 Number.isNaN 判断真正的 NaN,用 Number.isFinite 判断有限数字,用 Number.isSafeInteger 判断是否在安全整数范围内。金额计算不应直接依赖浮点小数。

详细版

经典例子:

console.log(0.1 + 0.2); // 0.30000000000000004
console.log(NaN === NaN); // false
console.log(Number.MAX_SAFE_INTEGER); // 9007199254740991
console.log(Number.isSafeInteger(9007199254740992)); // false

小数误差来自二进制无法精确表示某些十进制小数。0.10.2 在二进制中都是无限循环,只能近似存储。

金额常见做法是用整数分、定点数库或后端精确计算:

const priceCent = 1999;
const count = 3;
console.log(priceCent * count); // 5997 分

isNaN 会先做类型转换,Number.isNaN 不会,面试中更推荐后者。

完整版教学

一、Number 的底层模型

JavaScript 的 Number 是双精度浮点数,不区分 intfloatdouble

它大致由三部分组成:

sign | exponent | fraction
符号 | 指数     | 尾数

这让它能表示很大和很小的数,但不能精确表示所有十进制小数。

数值精度题不要只背 0.1 + 0.2,要说清楚“十进制小数转二进制时可能无限循环,只能近似存储”。

二、为什么 0.1 + 0.2 不等于 0.3

十进制的 0.1 转成二进制是无限循环小数,存进固定长度尾数时会被截断或舍入。

const sum = 0.1 + 0.2;

console.log(sum); // 0.30000000000000004
console.log(Math.abs(sum - 0.3) < Number.EPSILON); // true

Number.EPSILON 表示 1 附近两个可表示数之间的差距,可以用于小范围浮点比较。

三、安全整数是什么

安全整数范围是 -(2^53 - 1)2^53 - 1

常量
Number.MAX_SAFE_INTEGER9007199254740991
Number.MIN_SAFE_INTEGER-9007199254740991

超过这个范围,整数不一定能逐一精确表示。

console.log(9007199254740992 === 9007199254740993); // true

这就是订单号、雪花 ID、数据库大整数常转字符串的原因。

四、NaN 的判断方式

NaN 表示不是一个有效数字,它有一个特殊性质:不等于自身。

console.log(NaN === NaN); // false
console.log(Object.is(NaN, NaN)); // true

全局 isNaN 会做类型转换。

isNaN("abc"); // true
Number.isNaN("abc"); // false

如果要判断一个值本身就是 NaN,优先用 Number.isNaN

五、Infinity 和有限数字判断

除以 0 可能得到 Infinity-Infinity

console.log(1 / 0); // Infinity
console.log(Number.isFinite(1 / 0)); // false

全局 isFinite 也会做类型转换,因此更推荐 Number.isFinite

isFinite("123"); // true
Number.isFinite("123"); // false

六、业务计算怎么规避问题

金额、库存、积分这类业务不应直接用浮点小数做最终结算。

可选方案:

  • 用整数单位,例如金额用分
  • 使用 decimal / big number 库
  • 大整数 ID 用字符串传输
  • 浮点展示时只在显示层格式化
const amount = 0.1 + 0.2;
console.log(amount.toFixed(2)); // "0.30",只是格式化显示

toFixed 不是精确计算方案,它只是把结果格式化成字符串。

七、常见误区与追问

  • 误区:JavaScript 只有小数才有精度问题。 超过安全整数范围的整数也会有精度问题。
  • 误区:toFixed 可以解决金额计算。 它主要用于展示格式化,不应替代精确计算。
  • 误区:isNaNNumber.isNaN 完全一样。 前者会类型转换,后者更严格。
  • 误区:NaN === NaN 应该为 true。 NaN 按规范不等于自身。
  • 追问:为什么接口 ID 经常用字符串? 因为前端 Number 无法安全承载所有大整数。
  • 追问:浮点数比较应该怎么做? 可以判断差值是否小于可接受误差,而不是直接 ===

八、加强记忆

记住三组 API:

NaN      -> Number.isNaN
Infinity -> Number.isFinite
big int  -> Number.isSafeInteger

回答时先讲 Number 的浮点模型,再给 0.1 + 0.2 和安全整数两个数字例子,最后落到业务:金额用整数或精确库,大整数 ID 用字符串,展示格式化和计算精度要分开。