问题描述
在一次开发列表编辑页面时发现一个诡异的问题,列表展示的数据和回显的数据不一样,第一反应是接口出错了,查看了一下ajax请求,好像确实是这样:
列表请求接口:

数据回显请求接口:


很明显列表数据和回显数据不一致,于是就找服务端同学排查接口问题。服务端同学反馈从日志上看是没问题的,对比他们日志,发现日志打印的数据和界面展示的不一致,诡异的事情就这样发生了。更诡异的是直接在浏览器地址栏访问这个接口,返回的数据和日志的一样,和我界面展示的也不一致。

看样子应该是我项目中请求对数据做了处理,回过头再来看请求的数据,对比发现 driverId 数值,初步定为是因为数值类型精度丢失。
let num = 1001379549335920640
// 1001379549335920600
精度丢失原因
js数值类型遵循IEEE 754规范,占用64位,
s eeeeeee eeee ffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff
从左起
- 1位用来表示符号
- 11位用来表示指数
- 52表示尾数
这种表示方法直接导致了精度丢失,当数值无法用二进制表示时,就会采取0舍1入。
浮点数和大整数都会出现精度丢失。当大整数尾数大于 2^52 = 9007199254740992 时就可能精度丢失
9007199254740992 >> 10000000000000...000 // 共计 53 个 0
9007199254740992 + 1 >> 10000000000000...001 // 中间 52 个 0
9007199254740992 + 2 >> 10000000000000...010 // 中间 51 个 0
9007199254740992 + 1 // 丢失
// 9007199254740992
9007199254740992 + 2 // 未丢失
// 9007199254740994
9007199254740992 + 3 // 丢失
// 9007199254740996
9007199254740992 + 4 // 未丢失
// 9007199254740996
解决方案
- 如果数值本身不参与运算,可以换成字符类型
- 如果数值在常规范围内,可以先把小数放大到整数,运算后在缩小原来整数倍
// 0.1 + 0.2
(0.1*10 + 0.2*10) / 10 == 0.3 // true
- 如果对精度有严格要求,可以自己实现大数运算,或借助npm包(例如big.js)