跳到主要内容

如何解决 JavaScript 大整数精度丢失问题

·1 分钟

介绍 JavaScript Number 大整数精度丢失的原因,并通过在 JSON 解析和对象处理阶段将大整数字段转为字符串来避免精度问题

JavaScript 大整数 Number 精度丢失 JSON 解析 BigInt 处理

关于这个大精度丢失问题,一开始我还没在意,直到上班碰到了这个问题,当时记得在处理后端返回的大整数拿到一个超过 js 精度的 id 时,怎么传值都对不上,后面通过打印检查对比发现,我拿到的 id 跟我所获取到的不一致,当时还不知道是什么情况,后面上网搜索才知道这是属于一个精度丢失问题。

那原因是什么呢?
经过查询知道 JS 的 Number 类型遵循 IEEE754 标准,最大安全整数是 2^53 - 1
再大的数字会被强制四舍五入,导致精度丢失。

那我为了避免这种情况,后续在 JSON 解析和对象处理阶段,手动把大整数转换为字符串。下面是我这边写的一个工具方法。


问题示例:精度丢失 #

后端返回:

{
  "nxcallid": 1234567890123456789
}

如果直接 JSON.parse

JSON.parse('{"nxcallid":1234567890123456789}')
// => { nxcallid: 1234567890123456800 } 精度已丢失

核心思路:将大整数字段转为字符串再解析 #

只要能让 JSON 解析器把这些字段当作字符串读取,精度就能保留。


方法代码 #

1. 安全解析 JSON(处理大整数字段) #

export function safeJsonParse(jsonString: string, bigIntFields: string[] = ['nxcallid']): any {
  if (!jsonString) return null;
  
  let processedJson = jsonString;
  
  // 将大整数字段强制替换成字符串
  bigIntFields.forEach(field => {
    const regex = new RegExp(`"${field}":\\s*(\\d+)`, 'g');
    processedJson = processedJson.replace(regex, `"${field}":"$1"`);
  });
  
  try {
    return JSON.parse(processedJson);
  } catch (error) {
    console.error('JSON解析失败:', error);
    throw error;
  }
}

2. 处理数组中的大整数字段 #

export function processBigIntFields(data: any[], bigIntFields: string[] = ['nxcallid']): void {
  if (!Array.isArray(data)) return;
  
  data.forEach(item => {
    if (item && typeof item === 'object') {
      bigIntFields.forEach(field => {
        if (item[field] !== undefined && item[field] !== null) {
          item[field] = String(item[field]);
        }
      });
    }
  });
}

3. 处理对象中的大整数字段 #

export function processBigIntFieldsInObject(data: any, bigIntFields: string[] = ['nxcallid']): void {
  if (!data || typeof data !== 'object') return;
  
  bigIntFields.forEach(field => {
    if (data[field] !== undefined && data[field] !== null) {
      data[field] = String(data[field]);
    }
  });
}

使用示例 #

解析 JSON 字符串: #

const raw = '{"nxcallid": 1234567890123456789}';

const result = safeJsonParse(raw);
console.log(result.nxcallid);
// "1234567890123456789" ✔ 精度完好

处理数组: #

processBigIntFields(listData, ['userid', 'nxcallid']);

总结 #

JavaScript 精度问题不可避免,但可以通过简单规则避开,比如:

  • 避免让大整数进入 JS number 体系
  • 在解析 JSON 时,将大整数统一转为字符串
  • 对对象和数组中的大整数字段做二次处理