JavaScript 中所有数字都是 Number 类型(IEEE 754 双精度浮点数) ,64-bit 浮点数表示:
- 1 bit 符号
- 11 bit 指数
- 52 bit 小数位(有效位)
这导致 大数和小数都可能丢精度。
1.小数精度问题
原因
- 十进制小数无法精确表示为二进制
- 内存中只能存储近似值
示例
0.1 + 0.2 // 0.30000000000000004
0.1 + 0.7 // 0.7999999999999999
常见场景
- 金额计算
- 百分比或比例
- 累加循环
解决方案
- 整数化计算
(0.1*100 + 0.2*100) / 100 // 0.3 ✅
- 精度库:Big.js、Decimal.js
import Big from 'big.js';
Big(0.1).plus(0.2).toString() // "0.3"
- 展示用
toFixed或允许误差比较
Math.abs(a-b) < 1e-10
2.大整数精度问题
原因
- JS
Number最大安全整数:Number.MAX_SAFE_INTEGER = 2^53 - 1 ≈ 9e15 - 超过该范围会四舍五入丢精度
示例
9007199254740992 // 正确
9007199254740993 // 变成 9007199254740992 ❌
常见场景
- 数据库自增 ID
- 雪花 ID
- 时间戳(毫秒)
- 订单号、流水号
Network 上看到丢精度的原因
-
Response 标签显示原始文本,正确
-
Preview 或
res.json()解析时,浏览器自动 JSON.parse 转 Number- 超过 2^53-1 的整数丢精度
- Network Preview 显示的就是解析后的对象
解决方案
- 后端返回字符串(推荐)
{"id":"9223372036854775807"}
- 前端用
text()获取原始文本
const rawText = await fetch("/api/big-number").then(res => res.text());
- JSON-bigint 安全解析
const data = JSONbig.parse(rawText);
const idBigInt = BigInt(data.id.toString());
3.对比总结
| 类型 | 触发条件 | 示例 | 解决方法 |
|---|---|---|---|
| 小数 | 十进制无法精确转二进制 | 0.1 + 0.2 → 0.30000000000000004 | 整数化计算 / 精度库 / toFixed / 允许误差比较 |
| 大整数 | > 2^53-1 | 9223372036854775807 → 9223372036854775800 | 字符串 / BigInt / JSON-bigint |
4.开发实践建议
- 金额、ID、时间戳等精度关键数据,避免直接使用 Number
- 后端大整数统一返回字符串,前端解析安全
- 小数运算使用整数化或精度库,避免累积误差
- 数值比较不要直接用
===浮点数,使用误差范围 - Network Preview 上看到丢失精度 是浏览器解析造成,不代表后端错误
一句话总结
大数和小数精度问题都源自 Number 类型的表示限制,前端必须使用字符串、BigInt 或精度库保证精确值。