JS的Number限制 - 如何解决JSON序列化大数精度丢失问题
前言
JS 使用 IEEE 754 的双精度数表示数字,1 位符号,10 位指数,53 位底数。因此 JS 数字精度近似为 15.95 位 10 进制,也就是说整部加小数部分超过 15 位再用
Number
类型就不合适,就会出现精度丢失问题。
一. 原生JS大数精度丢失Demo
给出一个JSON串,里面有两个大数,一个是整数,一个是浮点数(JS同时用Number来表示这两个类型):
"{\"a\":258431607934229718,\"b\":1.258431607934229718}"
然后通过原生JSON.parse()
来看下会是什么样的结果:
console.log(JSON.parse("{\"a\":258431607934229718,\"b\":1.258431607934229718}"))
输出如下:
可以发现,无论是整数还是浮点数,都出现了精度丢失的情况。以整数为例,以718为结尾,通过JSON反序列化后变成了以730为结尾。此时可以佐证,JS的Number
类型确实存在大数精度丢失问题。
问题:那如果说在实际应用当中,某些业务就是需要这么大的数字来标识但却又存在大数精度丢失问题,该怎么办?
答:使用bigNumberJs
以及json-bigint
库来解决。
二. bigNumberJs解决运算丢失精度问题
安装bigNumberJS
:
npm install bignumber
在本篇文章中以简单的加法为例(其余操作可参考官方文档):
import BigNumber from 'bignumber.js';
const a = new BigNumber(0.2);
// 原生JS出现精度丢失
console.log(0.1 + 0.2) // 0.30000000000000004
console.log(a.plus(0.1)) // BigNumber { s: 1, e: -1, c: [ 30000000000000 ] }
console.log(a.plus(0.1).toString())// 0.3
console.log(a.plus(0.1).toNumber())// 0.3
结果如下:
三. json-bigint解决JSON序列化丢失精度问题
安装json-bigint
:
npm install json-bigint
案例如下:
import JSONBigInt from 'json-bigint';
const JSONBigIntNative = JSONBigInt();
// 原生JSON.parse,丢失精度
// { a: 258431607934229730, b: 1.2584316079342297 }
console.log(JSON.parse("{\"a\":258431607934229718,\"b\":1.258431607934229718,\"c\":123}"))
const obj = JSONBigIntNative.parse("{\"a\":258431607934229718,\"b\":1.258431607934229718,\"c\":123}")
console.log(obj)
console.log(obj.a.toString())
console.log(obj.b.toString())
console.log(obj.c)
结果如下:
可以看见通过JSONBigIntNative
反序列化得出的对象中,有精度相关的数字都会转成BigNumber
类型,那么在实际业务当中,我们只要对相关的字段做BigNumber
类型的处理即可。大数问题也就随之解决。