易学社
第二套高阶模板 · 更大气的阅读体验

JSON解析精度丢失?这些方法轻松搞定

发布时间:2025-12-31 18:01:29 阅读:179 次

做开发时经常要处理接口返回的 JSON 数据,尤其是金额、坐标、科学计算这类对精度要求高的场景。可有时候你会发现,明明接口传过来的是 123456789012345.67,结果一解析变成 123456789012345.66 或干脆是 123456789012345.7,精度就这么丢了。

问题出在哪?

根源在浮点数表示。JavaScript 中所有数字都是双精度浮点数(IEEE 754),能安全表示的整数范围是 -2^53 + 12^53 - 1,也就是 -90071992547409919007199254740991。超过这个范围,小数部分或末几位就可能被舍入。

比如下面这个 JSON:

{
  "orderId": 12345678901234567,
  "amount": 999999999999999.99
}

直接用 JSON.parse() 解析后,orderIdamount 都会出问题,因为它们超出了安全整数范围。

方案一:把大数转成字符串

最简单的方法是在服务端就把长数字当字符串传。虽然前端拿到的是字符串,但至少不会丢精度。

{
  "orderId": "12345678901234567",
  "amount": "999999999999999.99"
}

前端再根据需要转成 BigInt 或使用专门的高精度库处理。

方案二:自定义 parse 函数,拦截数字字段

如果不能改接口,可以在解析时手动控制。利用 JSON.parse 的第二个参数——还原函数(reviver)。

const data = `{"id":12345678901234567,"price":999999999999999.99}`;

const result = JSON.parse(data, (key, value) => {
  // 假设 id 和 price 需要高精度
  if (key === 'id' || key === 'price') {
    return String(value); // 转成字符串保留原样
  }
  return value;
});

console.log(result); // { id: '12345678901234567', price: '999999999999999.99' }

这样就能在解析阶段就把关键字段“保护”起来,避免被当成 JS 数字处理。

方案三:用 BigInt 处理超大整数

如果是纯整数,比如订单号、用户 ID,可以考虑用 BigInt

const result = JSON.parse(data, (key, value) => {
  if (key === 'id') {
    return BigInt(value);
  }
  return value;
});

console.log(result.id); // 12345678901234567n

注意 BigInt 不能和普通数字混算,运算时要统一类型。

方案四:引入高精度计算库

涉及金额计算时,推荐用 decimal.jsbig.js 这类库。

import Decimal from 'decimal.js';

const result = JSON.parse(data, (key, value) => {
  if (key === 'price') {
    return new Decimal(value);
  }
  return value;
});

// 后续计算就不会有浮点误差
const total = result.price.times(2);

这类库内部用字符串模拟十进制运算,完全避开浮点数坑。

实际项目中的建议

在公司做支付系统时,我们就吃过这亏。一开始订单金额用 number 传,测试时好好的,上线后发现几毛钱对不上,查了半天才发现是 JSON 解析时自动转 float 导致的舍入。

后来统一改成金额字段用字符串传输,前端收到后用 Decimal 处理,再也没出过问题。团队也加了代码规范:涉及金额、ID、地理坐标等,一律不信任原始 number 类型。

接口文档也明确标注哪些字段是“高精度数值”,后端输出时尽量配合做字符串化处理。