axios拦截器处理long精度丢失问题

2,011 阅读2分钟

如果你在 Chrome Dev Tools 控制台中输入 JSON.parse('{"taskid": 9007199254740993}') 运行结果返回的将会是 {taskid: 9007199254740992}。为什么 parse 后的数值会不一致?

双精度浮点数 IEEE 754

JavaScript 采用双精度浮点数( IEEE 754 标准)来表示它的 Number 类型。一个数字占用 64 bits 存储空间(这里的每一位都只能存放 0 或 1):

第一位 0 表示正值、1 表示负值;第 2- 12 位表示 2 的指数部分(可正可负);剩下的 52 个 bits 表示尾数部分,它的长度决定了数字的精度。

(−1)sign×2exponent−0x3ff×1.mantissa

如果我们将符号位和指数位共 12 个 bits 表示为 16 进制(4 个二进制 bits 1111 得到 1 个 16 进制的 f),那么它的取值范围为 [000, 7ff]。其中,规范约定当取值 7ff 时,可以表示无穷大或 NaN。

所以双精度浮点数能表示的最大 16 进制数为 0x7fef_ffff_ffff_ffff,转为十进制约为 1.79 ×10 的 308 次方。能表示的数的范围非常大,但受限于尾数的长度,能“精确”表示的数字并不多,我们来看看这个数到底是多少。

最大安全整数

从以上表示公式我们能看到,当指数部分只取 1 位,尾数部分取满 52 位时,可以精确表示出 JavaScript 里的整数,其 16 进制形式为 0x001f_ffff_ffff_ffff ,即 9007199254740991

它等于 2 的 53 次方减 1,在 ES6 中,可以通过 Number.MAX_SAFE_INTEGER 引用到这个数值。

Number.MAX_SAFE_INTEGER === Math.pow(2, 53) - 1  // true
Number.MAX_SAFE_INTEGER === 0x001f_ffff_ffff_ffff   // true
Number.MAX_SAFE_INTEGER === 9007199254740991     // true
Number.MAX_SAFE_INTEGER === -Number.MIN_SAFE_INTEGER  // true

超过这个最大安全整数的运算,都可能因为发生进位溢出,造成精度丢失。

前后端大数传输方案

  • 后端将long类型统一作为string类型序列化
@Bean("jackson2ObjectMapperBuilderCustomizer")
public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
    return jacksonObjectMapperBuilder -> jacksonObjectMapperBuilder
            .serializerByType(BigInteger.class, ToStringSerializer.instance)
            .serializerByType(Long.class, ToStringSerializer.instance)
            .serializerByType(Long.TYPE, ToStringSerializer.instance);
}
  • 前端axios拦截器通过json-bigint库处理
//导入
import JSONbig from 'json-bigint'

//可以通过axios的transformResponse方法,这个方法的作用是在传递给then/catch前,允许修改响应数据
  
//axios在这里默认把响应体从json字符串转成了js对象
axios.defaults.transformResponse = [function (data) {
     try {
          return JSONbig.parse(data)
     }catch(){
         return data;
     }
 }]
//如果不写这个方法,返回的数据是自动就转为json对象的了,如果写了,就要自己转为对象,即return JSON.parse(data)

json-bigint会把超出 JS 安全整数范围的数字转为一个 BigNumber 类型的对象,对象数据是它内部的一个算法处理之后的,我们要做的就是在使用的时候转为字符串来使用。

参考

www.npmjs.com/package/jso…