起因
有一个数字104647149756747776。 一使用就会被js修改值,因为它超过了js的最大安全数。
可以看到,这个数值大于Number.MAX_SAFE_INTEGER两位。按照js的说法:
Number.MAX_SAFE_INTEGER常量表示在 JavaScript 中最大的安全整数(maxinum safe integer)(253 - 1)。这里安全存储的意思是指能够准确区分两个不相同的值,例如Number.MAX_SAFE_INTEGER + 1 === Number.MAX_SAFE_INTEGER + 2 将得到 true的结果,而这在数学上是错误的,参考Number.isSafeInteger()获取更多信息.
我的理解是大于这个数的数值,可能会被四舍五入,反正就是不保证它是正确的了。
需求
现在我需要把这样一个超长的数值通过ajax传递给后台,还需要保证精度。
先说结论 : 实现不了。期间我尝试使用bignumber.js 和 json-bigint两个库去处理数字,都能起到一定的作用,但是不能将数字原封不动的传递到后台。
bignumber
bignumber是一个封装库,它还是改变不了js number类型的最大值。也就是说它还是会被处理。不过bignumber可以帮你保留数字形式,不至于被转成科学计数法。
let a = new BigNumber(104647149756747776)
console.log(a.toFixed()) // 104647149756747780
let b = new BigNumber(111111111111111111111111111)
console.log(b.toFixed()) // 111111111111111100000000000
如果你的数值是小于Number.MAX_SAFE_INTEGER的,bignumber就很方便了,可以帮你处理小数和进行高精度的运算。
json-bigint
json-bigint是一个对json序列化,反序列化的库。它依赖了bignumber,同时还支持bigInt数据类型。
bigInt es10 增加的一种数字类型(大整数嘛),特点是大数字,缺点就是不能存小数,不能和number运算,不能被json库序列化,反序列化。
json-bigint就是用来对存在大整数的json进行序列化,反序列化的。我试了一下,可以正常处理104647149756747776这个长度的数字,不会丢精度。只是业务中咱也不能强制用户只能输入整数,然后我用的还是juery ajax。我在github上没找到通过js引入的方法,不过我用react项目尝试了一下,还是把案例放出来吧。
官方示例
``
var JSONbig = require('json-bigint');
var JSONstrict = require('json-bigint')({ strict: true });
var dupkeys = '{ "dupkey": "value 1", "dupkey": "value 2"}';
console.log('\n\nDuplicate Key test with both lenient and strict JSON parsing');
console.log('Input:', dupkeys);
var works = JSONbig.parse(dupkeys);
console.log('JSON.parse(dupkeys).dupkey: %s', works.dupkey);
var fails = 'will stay like this';
try {
fails = JSONstrict.parse(dupkeys);
console.log('ERROR!! Should never get here');
} catch (e) {
console.log(
'Succesfully catched expected exception on duplicate keys: %j',
e
);
}
// 这些事打印内容
Duplicate Key test with big number JSON
Input: { "dupkey": "value 1", "dupkey": "value 2"}
JSON.parse(dupkeys).dupkey: value 2
Succesfully catched expected exception on duplicate keys: {"name":"SyntaxError","message":"Duplicate key \"dupkey\"","at":33,"text":"{ \"dupkey\": \"value 1\", \"dupkey\": \"value 2\"}"}
我的示例
import React from "react";
import { Button } from "antd";
import jsonBig from 'json-bigint'
export default function Home(): JSX.Element {
var json = '{ "value" : 104647149756747776, "v2": 123 }';
var json2 = '{ "value" : 10464714975674742342342423776, "v2": 123 }';
console.log('Input:', json);
console.log('');
console.log('node.js built-in JSON:');
var r = JSON.parse(json);
console.log('JSON.parse(input).value : ', r.value.toString());
console.log('JSON.stringify(JSON.parse(input)):', JSON.stringify(r));
console.log('\n\nbig number JSON:');
var r1 = jsonBig.parse(json);
var r2 = jsonBig.parse(json2);
console.log('JSONbig.parse(input).value : ', r1.value.toString());
console.log('JSONbig.stringify(JSONbig.parse(input)):', jsonBig.stringify(r1));
console.log('r2 JSONbig.parse(input).value : ', r2.value.toString());
console.log('r2 JSONbig.stringify(JSONbig.parse(input)):', jsonBig.stringify(r2));
return <Button> Home页面</Button>;
}
可以看到对数字104647149756747776 的处理很正常,然后我随意输入的超长数字被转化为科学计数法了。
我的疑问
我对前端了解不太多,网上也没找到将科学计数法形式的数字转为number,还不丢失精度的。不知道能不能转回来。
其他解决办法
现在用超长数字的,大部分是雪花算法生成的id。然后普遍的解决办法就是前端换成字符串,后端再转换为数字。
》可以看看这个 牛气的JavaScript,让雪花算法成为空气