Go Json Unmarshal(反序列化) 丢失数字精度

现象

  • 业务会使用 id生成器 产生的 分布式唯一ID,长度比较长。代码反序列化时,出现精度丢失,导致线上故障。
go 复制代码
package main

import (
   "testing"
   "time"
   "github.com/bytedance/sonic"
   "github.com/stretchr/testify/assert"
)

func TestPrintAttr(t *testing.T) {
   amap := map[string]any{
      "psm_businessline_ref": map[string]any{
         "id": 1691071059696833999,
      },
   }

   amapStr, err := sonic.MarshalString(amap)
   assert.Nil(t, err)

   t.Log("\n", amapStr)

   m1 := make(map[string]any)
   err = sonic.UnmarshalString(amapStr, &m1)
   assert.Nil(t, err)
}

原因

  1. 反序列化时,对于数值类型的value,默认会反序列化成float64类型。
  2. float64可以存储的最大整数是52位尾数全位1且指数部分为最大 0x07FEF FFFF FFFF FFFF
ini 复制代码
(0x001F FFFF FFFF FFFF)16 = (9007199254740991)10 
(0x07EF FFFF FFFF FFFF)16 = (9218868437227405311)10 

也就是理论上数值超过9007199254740991(长度=16)就可能会出现精度缺失。

10进制数值的有效数字是16位,一旦超过16位大概率会有缺失精度的问题

一般分布式唯一id是20位长度,所以必然出现精度缺失。

参考:

解决方案

  • 使用 json.Decoder 来代替 json.Unmarshal 方法
go 复制代码
package main

import (
   "testing"
   "time"
   "github.com/bytedance/sonic"
   "github.com/stretchr/testify/assert"
)


func TestPrintAttr(t *testing.T) {
   amap := map[string]any{
      "psm_businessline_ref": map[string]any{
         "id": 1691071059696833999,
      },
   }

   amapStr, err := sonic.MarshalString(amap)
   assert.Nil(t, err)

   t.Log("\n", amapStr)

   rightM := make(map[string]any)
   if len(amapStr) > 0 {
      de := jsoniter.NewDecoder(bytes.NewReader([]byte(amapStr)))
      de.UseNumber()
      err := de.Decode(&rightM)

      if err != nil {
         t.Fatal(err)
      }
   }
   
}

json.Number本质是string,反序列化的时候将json的数值转成字符串 ,而字符串不会有精度丢失问题,所以没有问题。json.Number如下:

go 复制代码
package json
// A Number represents a JSON number literal.
type Number string
相关推荐
程序员大阳18 小时前
副队长JavaScript教程(65)-详解JSON:轻量级数据交换格式
javascript·json·序列化·反序列化·stringify·parse
hourinX20 小时前
从 v1 到 v2,我重新拆了头像生成的整条链路—— gdenticon 重构日志(二)
go
FungLeo1 天前
成为全栈·Go 后端篇·分类树与标签关系:目录变更为何需要协调
golang·go·gorm·数据库事务·分类树·成为全栈
FungLeo1 天前
成为全栈·Go 后端篇·文章查询与展示投影:安全分页、排序和软删除
golang·go·gorm·分页查询·成为全栈·sql安全
光影6271 天前
Unittest单元测试基础速记
测试工具·单元测试·json·测试覆盖率
ZLongTeng1 天前
EIDE的launch.json文件
json
梦想不只是梦与想2 天前
Python 中的 json 模块
python·json·对象转字符串
步行cgn2 天前
JSON.stringify 详解:把 JavaScript 对象变成 JSON 字符串
开发语言·javascript·json
KevinAshen672 天前
用 Go 修复旧下载工具:从接口 403 到凭证截断的完整复盘
go·github
阿正的梦工坊2 天前
深入理解 JSON-RPC 2.0:轻量级远程调用协议详解
rpc·json