查看原文
其他

避坑 | 记一次前端长整数精度丢失问题

鱼皮 程序员鱼皮 2022-08-14
前几天,鱼皮在工作时遇到一件很奇怪的事。
分享给大家,避坑避坑~

孽起

在前后端联调时,发现后端有一个接口返回的值和前端页面上展示的值不一致。
后端Java实现的接口如下,返回一个json格式的大整数 123456789123456789
@RestController@RequestMapping("/test")public class YupiTestController {
@GetMapping public Long getNum() { return 123456789123456789L; } }

但是前端请求这个接口后,在界面上展示的却是 123456789123456780,最后一位是0而不是9!


问题定位

后端同学利用curl工具测试自己的接口,得到的数据完全正确。

前端同学打开浏览器的开发者工具(F12)查看网络请求(注意要查看请求原生的返回值,而不是被浏览器二次处理过的格式化数据),发现后端返回的数据完全正确。

既然后端数据返回正确,那就是前端的锅没跑了。

可是前端明明拿到后端返回的json数据,解析成数字就直接展示了,为什么最后一位数字展示错误呢?


发现元凶

比对分析接口返回和前端展示的数据,发现只有数字超过16位时,才会出现最后几位数字不一致的问题。

难道是数字太大了,发生了精度丢失?

Java语言中的Long类型是64位,难道前端Js语言的Long类型小于64位?

等等,Js好像没有Long类型!

那就百度一下Js的数字类型,终于发现了问题的元凶。


Js的Number类型

在Js中,用Number来表示数字类型的值。Number类型总长度64位二进制bit,使用53位表示小数位,10 位表示指数位,1 位表示符号位。因此,Number整数的表示范围为 -2^53 ~ 2^53(不包含两端)

可以在控制台打印Number的最大和最小值:

Number最大值

Number最小值

在其他语言,如Java中,Long类型占64位二进制bit,最大值为:9223372036854774807(2^63 - 1)长度约19位。

而在Js中,由于Number类型的值也包含了小数,最大值为:9007199254740993(2^53 - 1)长度约16位。

因此当Java返回超过16位的Long型字段转为json时,前端Js得到的数据将由于溢出而导致精度丢失。

既然知道了出现问题的原因,解决问题就很简单了。


如何解决?

虽然前端也可以解决问题,比如通过正则表达式解析替换、或者修改json parser,但比较麻烦,更推荐在后端解决

非常简单,将可能超出范围的数字类型(Long)变量转为字符串类型(String)即可!


您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存