欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

JS大坑之19位数的Number型精度丢失问题详解

程序员文章站 2022-06-30 11:30:54
more 本项目仅供爬取体验,每次访问都会实时爬取数据,所以数据返回速度会比较慢,实际操作应该是定时爬取数据然后将数据存进数据库,数据从数据库返回从而提高数据返回效率。...

more

本项目仅供爬取体验,每次访问都会实时爬取数据,所以数据返回速度会比较慢,实际操作应该是定时爬取数据然后将数据存进数据库,数据从数据库返回从而提高数据返回效率。

但项目很基础,可以作为以上各个node模块最基础的练手使用,希望可以帮到大家 ????起源

最近在实现一个需求的时候,需要接入第三方的接口,先调用a接口,a接口返回的数据里,有一个taskid,然后再使用这个taskid请求b接口,获取最终需要的数据。

后端使用的是node,因此最开始使用的是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,再用新的taskid请求b接口,结果却是正常的!

我在反复检查代码,确认请求的参数都是正常的格式之后,一时陷入了无尽的沉思之中。。。

发现

在做了几次尝试之后,我发现使用node请求得到的taskid最后两位数都是0,即1152921504735848700,而使用postman获取的taskid,则是比较正常的是1152921504735848759,接着我在node控制台做如下操作

JS大坑之19位数的Number型精度丢失问题详解

就是这么一瞬间,顿悟了。a接口里的taskid是个19位数字,而request-promise在将数据解析成json时,导致这个19位的数字丢失了精度,查了下资料,发现js的number类型有个最大安全值,即2的53次方(9007199254740992),超过这个值就会出现精度丢失的问题。 orz

获取正确的响应数据

由于在一开始使用request-promise包,因此获取的taskid是丢失了精度了,因此改用了node原生的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(),又会引起精度丢失的问题,这可真尴尬 orz

如果这个接口是已方可控的,那么就可以把这个19位数的number转成字符串,这样在解析的时候就不会出错了,但是由于是第三方接口,因此没法改变。那么最快的解决方案,就是换种编程语言请求啊╮(╯_╰)╭

最后的解决

好吧,最后还是用了node,不过我用了比较硬核的方案实现,先在获取的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 
// }

结语

使用node也有一段时间了,因为涉及不到大数计算,因此对于编号啊,id啊,都是用字符串形式进行存储的,也就一直没有遇到这个问题。这一次居然碰上了,不得不说js在这一方面确实有点弱势,之后也尝试了下使用go,python进行请求,都是能正确解析不过node使用起来还是很舒服的

以上所述是小编给大家介绍的js大坑之19位数的number型精度丢失问题详解整合,希望对大家有所帮助