JS 大数溢出问题

204 天前
 justdoit123
后端的大数到 JS 端 JSON.parse 之后,经常大数溢出。

这个问题之前一直是在后端看到一处,就 stringify 一处。现在实在是觉得烦了,想请教下各位有什么更好的做法?

查了些资料,这个溢出是在 JS 进程里 JSON.parse 的时候发生的,跟 JS 自身有关系,跟 JSON 没有关系。想着,在 JSON.parse 的时候,换成 BigInt 的 JSON.parse 。但是这其中也有两种策略:

1. 只要是整数,全部转成 BigInt ,不管实际会不会溢出。这样的好处是统一,坏处怕会有什么性能问题
2. 只有当一个整数会溢出的时候,才会转成 BigInt 。但是这样用的时候还需要做下判断,比较麻烦。

暂时想在内部后台系统里尝试下,浏览器兼容不成问题。有没有别的高招?

PS. 这个真是 JS 的天坑。
3260 次点击
所在节点    JavaScript
47 条回复
realJamespond
204 天前
让后端返回字符串再处理?
neotheone2333
204 天前
返字符串,内部再转 Bignumber.js 处理
justdoit123
204 天前
@realJamespond 就是不想再在后端转字符串了呀!

除非从 DB Access 层就把所有 bigint 都转成 string ,顺便好奇问下大家也是这么做的吗?像 timestamp 这种,在 db 里用 bigint 存储,但是使用的时候是实实在在要当数字使用的,如果转成 string 用的时候还要转回去。
debuggerx
204 天前
这个很多语言都有类似的问题,最简单的还是让后端把可能溢出的字段用字符串类型传过来,前端自己转
zjsxwc
204 天前
如下,JSON 里整数是 64 位的,但到了 js v8 里却不支持 64 位整数,目前主流 cpu 都是 64 位,64 位整数最大值是~(1<<63) = (1<<63) -1 = 9223372036854775807:

获得 json 格式的字符串
```
$ php -r "var_dump(json_encode(9223372036854775807));"
string(19) "9223372036854775807"
$ php -r "var_dump(json_encode(9223372036854775808));"
string(21) "9.223372036854776e+18"
```

js 解析就丢失精度了,9223372036854775807 变成了 9223372036854776000 ,最后几位全变 0 了:
```
JSON.parse("9223372036854775807")
9223372036854776000
```
cmdOptionKana
204 天前
唉,基础知识不能叫做“天坑”吧,浮点数处理本来就有很多注意事项,编程语言是设计给“专家”使用的,本来就不是面向 end user 的。

如果不是金融相关的,多数情况下都可以降低精度,如果确实需要很高精度,那也只好麻烦一点处理了。
icoomn
204 天前
之前也遇到这个问题,SQL 语句 select count() 查出来的数据默认就是 bigint 类型, 我是在后端直接做类型转换,将 bigint 转为 int 然后再返给前端的。

前端解决的话可以看下这个 JS 库:json-bigint
ZAnko
204 天前
我们也是后端处理成字符串返回的,如果一定要前端处理,可以试试利用第三方库在相应拦截中统一处理掉。
iOCZ
204 天前
一些第三方库(如 json-bigint )之所以能正确的处理大数 parse ,且不造成精度丢失,其实现原理也是类似。在拿到接口的 JSON 数据时,并不直接 JSON.parse ,而是先将整块数据当作 text 字符串,将其中的大数以 string 类型进行存储和标记,再使用定制化的 JSON.parse 。

自己处理的话,不外乎类似如此,可以单独抽取一个方法包裹 JSON.parse:
```javascript
var text = '{ "name":"Bill Gates", "birth":"1955-10-28", "city":"Seattle"}';
var obj = JSON.parse(text, function (key, value) {
if (key == "birth") {
return new Date(value);
} else {
return value;
}});
```
justdoit123
204 天前
转成 string 给前端,前端送回给后端的时候,后端得再转回 int ( python 后端),现在其实就是这么做的。就是时不时会遗漏掉,而且这种问题是要等溢出你才会发现。基于此,想寻找一个更好的方案。
Morii
204 天前
我是后端,bigint 都是 parseString 给到前端的。
Terry166
204 天前
npm install --save bn.js
webbillion
204 天前
之前的方案是前端涉及数字都当 string 处理,后端也返回 string ,至于后端怎么处理可以避免忘记,不清楚后端怎么处理的,不知道 python 有没有前端 decimal.js 这种库,涉及数字全部用单独的库,而不是 原生 int ,也许有用?
mxT52CRuqR6o5
204 天前
后端框架不能直接指定大数类型序列化成 string 吗?只能一处处手动改?
thinkershare
204 天前
这个和 JS 没有一毛钱关系,你要怪只能怪 ECMA 规范和 IEEE64 浮点数规范,还有 JSON 规范。
justdoit123
204 天前
@mxT52CRuqR6o5 可以呀。

问题是,不是所有的 bigint 转成 string 都能相安无事。

比如,如果这个数字是用来做 ID 之类的,那它是 string 也无所谓,因为很少会对 ID 做什么加减乘除的运算。

但是这个 bigint 可能是表示毫秒、表示钱,这时候转成 string 就很不方便。而且后端又不是只为 js 服务,还有 ios 跟 android 。
RedBeanIce
204 天前
@mxT52CRuqR6o5

可以,但是最好不要这样子。

一个后端+略微入门前端的人认为,一个语言不支持大数,是不太合理的。
clue
204 天前
json-bigint +1

有现成的库了,不需要自己去处理,前后端都换用`json-bigint`就解决了
debuggerx
204 天前
该说的楼上都说得差不多了,再加一个后端死活就不改,前端又不好用库的时候的一个骚操作吧:

let jstr = '{"asd": 9223372036854775807}';
console.info(JSON.parse(jstr));
console.info(JSON.parse(jstr.replace(/\"asd\":[ ]?(-?[\d|\.]+)/, (ma, p) => ma.replace(p, `"${p}"`))));
console.info(JSON.parse('{"asd": -12345.6789}'.replace(/\"asd\":[ ]?(-?[\d|\.]+)/, (ma, p) => ma.replace(p, `"${p}"`))));
就是把 json 字符串先根据 key 把数字正则替换成字符串🐶

为了骚而骚,别用,除非是为了给别人埋坑~
jazzg62
204 天前
我是手动解析了后端请求,从网上找了 JSON.parse 的 polyfill ,改了实现,对超过精度的数字项改成字符串

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/983013

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX