解决Go Json Unmarshal反序列化丢失数字精度问题
作者:cli
业务会使用 id生成器 产生的 分布式唯一ID,长度比较长,所以代码反序列化时,会出现精度丢失问题,那如何解决呢,下面小编就来和大家详细讲讲
现象
业务会使用 id生成器 产生的 分布式唯一ID,长度比较长。代码反序列化时,出现精度丢失,导致线上故障。
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) }
原因
- 反序列化时,对于数值类型的value,默认会反序列化成
float64
类型。 - float64可以存储的最大整数是52位尾数全位1且指数部分为最大
0x07FEF FFFF FFFF FFFF
(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 方法
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
如下:
package json // A Number represents a JSON number literal. type Number string
到此这篇关于解决Go Json Unmarshal反序列化丢失数字精度问题的文章就介绍到这了,更多相关Go Json Unmarshal内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!