JS踩坑实战之19位数Number型精度丢失问题详析

作者:后端茶话时间:2026-10-25 21:19:24浏览:343

本文详解JavaScript中19位数字因超过最大安全值导致的精度丢失问题。通过Node.js第三方库request-promise与原生http模块的对比,以及JSON.parse()处理长整数的陷阱,提供了在无法修改第三方接口时,通过正则替换引号保持精度的硬核解决方案。

前言

本文深入探讨JavaScript中19位Number类型数据的精度丢失陷阱,通过具体示例代码进行详细解析,为开发者在学习或实际工作中提供有价值的参考,欢迎有兴趣的读者共同交流探讨。

近期在开发一个业务需求时,需要对接第三方接口。流程是先调用A接口,从返回数据中获取taskId,随后使用该taskId请求B接口以获取最终所需数据。

后端环境基于Node.js,起初采用request-promise库发起第三方接口请求。然而,在获取A接口返回的taskId并调用B接口后,B接口返回了系统错误。简易实现代码如下:

const rp = require('request-promise')
const { taskId } = await rp('https://xxx.com/A')
const options = {
   method: 'POST',
   uri: 'https://xxx.com/B',
   body: {
    taskId
  },
   json: true
}
const result = await rp(options) 
// {
//  "errorcode": "40001",
//  "message": "系统错误",
//  "status": "failed"
// }

随后,我使用Postman手动请求A接口获取新的taskId,再用该ID请求B接口,结果却显示正常。

在反复检查代码,确认请求参数格式均无误后,问题原因一度令人困惑。

发现

经过多次尝试,我发现通过Node.js请求获取的taskId最后两位数均为0,例如1152921504735848700;而通过Postman获取的taskId则显示为正常的1152921504735848759。接着,我在Node控制台进行了如下操作验证:

前言

此时恍然大悟。A接口返回的taskId是一个19位数字,而request-promise在将其解析为JSON格式时,导致该19位数字丢失了精度。查阅资料得知,JavaScript的Number类型存在最大安全值,即2的53次方(9007199254740992),超过此值便会出现精度丢失。

获取正确的响应数据

由于初始使用了request-promise库,获取的taskId已丢失精度,因此改用Node.js原生http模块发送请求。

const req = https.request('https://xxx.com/A', (res) => {
  res.on('data', (chunk) => {
  // 由于这里获取到的响应数据是JSON字符串,因此19位的数字只是字符串的一部分,这时获取到的taskId就是正确的数字
   console.log(`BODY: ${chunk}`);
  });
  res.on('end', () => {
   console.log('No more data in response.');
  })
 })

虽然成功获取到正常的响应数据,但这是一个JSON字符串。接下来需要将其解析为JSON对象,然而直接使用JSON.parse()又会引发精度丢失问题,情况颇为棘手。

若该接口由我方控制,可将19位数字转为字符串以避免解析错误。但由于是第三方接口,无法修改其返回格式。最快的替代方案似乎是更换编程语言发起请求。

最后的解决

最终仍决定使用Node.js,但采用了一种较为硬核的方案:在获取的JSON字符串中定位该19位数字,并为其添加双引号。这样在使用JSON.parse()解析时,即可保持数值正常,后续流程得以顺利执行。代码如下:

let result = '{"taskId":1152921504735848759,"status":"CREATED","progress":0.0,"success":true}'
// JSON.parse(result) 不为19位数补上双引号,直接parse时,精度丢失,结果如下:
// { 
//  taskId: 1152921504735848700,
//  status: 'CREATED',
//  progress: 0,
//  success: true 
// }
const taskId = result.match(/[0-9]{19}/)[0] // 正则获取19位数字的值
result = result.replace(taskId,`"${taskId}"`) // 补上双引号
const data = JSON.parse(result) 
// { 
//  taskId: '1152921504735848759', // 解析出来之后是字符串,因此没有丢失精度
//  status: 'CREATED',
//  progress: 0,
//  success: true 
// }

总结

至此,关于JS中19位Number类型精度丢失问题的实战分析介绍完毕。更多相关JS Number类型精度丢失问题内容,请搜索本站历史文章或继续浏览后续相关文章,希望大家以后多多支持本站!

作者最新文章