Skip to content

后端返回的64位大数,前端怎么处理精度丢失的问题 #72

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
Twlig opened this issue Mar 24, 2022 · 0 comments
Open

后端返回的64位大数,前端怎么处理精度丢失的问题 #72

Twlig opened this issue Mar 24, 2022 · 0 comments

Comments

@Twlig
Copy link
Owner

Twlig commented Mar 24, 2022

双精度浮点数

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

image

最大安全整数

当表示整数时,js只使用52位尾数来表示,最大为2^53-1,即9007199254740991,超过这个数就会发生进位溢出等错误。

后端大数传输问题

而后端如果采用bigint 类型字段,在 MySQL 中,一个 bigint 存储占用 8 Bytes 的空间,即 64 bits。当取值为无符号整型时,能表示的范围是 0 到 2 的 64^-1,即 18446744073709551615

当取值在 (9007199254740991, 18446744073709551615] 之间时,后端程序通常能正确的执行 JSON 序列化操作,并通过 HTTP 接口返回给前端,而前端执行 JSON.parse 解码时,会因为语言本身的限制发生精度丢失,引发 bug。

大数转字符串类型

为了解决大数传递精度丢失的问题,常见的方案是“将大数转为字符串类型”。具体的做法如下:

  • 后端程序先将大数转为 string 类型,再进行 JSON encode,传给前端。
  • 前端拿到数据后 decode 成 string 类型,直接展示。
  • 当需要大数运算时,将 string split 成多段安全整数字符串,每段单独转为 number 类型,在安全范围内计算完成后,再 join 成 string 类型进行展示。

一些第三方库(如 json-bigint)之所以能正确的处理大数 parse ,且不造成精度丢失,其实现原理也是类似。

  • 在拿到接口的 JSON 数据时,并不直接 JSON.parse
  • 先将整块数据当作 text 字符串,将其中的大数以 string 类型进行存储和标记
  • 使用定制化的 JSON.parse

类型语义丢失

上述处理其实会带来string和number类型混乱问题,无法得知当前传递的数据到底是以string类型,还是要以大数类型处理。

前端往后端 POST 数据时,有两种常见的编码形式 application/x-www-form-urlencodedapplication/json

当需要传递一个 number 类型的 id 给接口时,application/x-www-form-urlencoded 在 HTTP Request Body 中传输的是 id=1,而 application/json 的 Body 则是 {"id":1} 。之所以认为后者的语义更好,是因为后者能正确地反映出 id 的真实类型为 number。

而当这个 id 为 String 类型时,前者传输的依然是 id=1,后者则变为了 {"id":"1"}。对于后端程序来说,这层类型语义能让参数类型校验和计算更加准确和方便。

而如果前后端采用将“大数转为字符串”的方案,当 taskid 以 string 类型返回时,调用方将无法判断出它在业务和 DB 中到底是 char 字符类型存储的,还是 bigint 类型存储,导致类型语义丢失的情况发生。

参考文章:JSON Bigint大数精度丢失的背后

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant