解決Go?Json?Unmarshal反序列化丟失數(shù)字精度問(wèn)題
現(xiàn)象
業(yè)務(wù)會(huì)使用 id生成器 產(chǎn)生的 分布式唯一ID,長(zhǎng)度比較長(zhǎng)。代碼反序列化時(shí),出現(xiàn)精度丟失,導(dǎo)致線上故障。

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)
}原因
- 反序列化時(shí),對(duì)于數(shù)值類型的value,默認(rèn)會(huì)反序列化成
float64類型。 - float64可以存儲(chǔ)的最大整數(shù)是52位尾數(shù)全位1且指數(shù)部分為最大
0x07FEF FFFF FFFF FFFF
(0x001F FFFF FFFF FFFF)16 = (9007199254740991)10
(0x07EF FFFF FFFF FFFF)16 = (9218868437227405311)10
也就是理論上數(shù)值超過(guò)9007199254740991(長(zhǎng)度=16)就可能會(huì)出現(xiàn)精度缺失。
10進(jìn)制數(shù)值的有效數(shù)字是16位,一旦超過(guò)16位大概率會(huì)有缺失精度的問(wèn)題
一般分布式唯一id是20位長(zhǎng)度,所以必然出現(xiàn)精度缺失。
參考:
解決方案
使用 json.Decoder 來(lái)代替 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本質(zhì)是string,反序列化的時(shí)候?qū)?strong>json的數(shù)值轉(zhuǎn)成字符串,而字符串不會(huì)有精度丟失問(wèn)題,所以沒(méi)有問(wèn)題。json.Number如下:
package json // A Number represents a JSON number literal. type Number string

到此這篇關(guān)于解決Go Json Unmarshal反序列化丟失數(shù)字精度問(wèn)題的文章就介紹到這了,更多相關(guān)Go Json Unmarshal內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Go使用Gin+mysql實(shí)現(xiàn)增刪改查的詳細(xì)實(shí)例
golang本身沒(méi)有提供連接mysql的驅(qū)動(dòng),但是定義了標(biāo)準(zhǔn)接口供第三方開(kāi)發(fā)驅(qū)動(dòng),下面這篇文章主要給大家介紹了關(guān)于Go使用Gin+mysql實(shí)現(xiàn)增刪改查的相關(guān)資料,需要的朋友可以參考下2022-12-12
詳解golang開(kāi)發(fā)中select多路選擇
這篇文章主要介紹了golang開(kāi)發(fā)中select多路選擇,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-09-09
golang中l(wèi)og包自定義輸出日志格式與寫(xiě)入到文件
這篇文章主要給大家介紹了關(guān)于golang中l(wèi)og包自定義輸出日志格式與寫(xiě)入到文件的相關(guān)資料,日志輸出在任何項(xiàng)目中都極其重要,是有助于后續(xù)我們排查解決程序BUG,需要的朋友可以參考下2023-06-06
go-micro開(kāi)發(fā)RPC服務(wù)以及運(yùn)行原理介紹
這篇文章介紹了go-micro開(kāi)發(fā)RPC服務(wù)的方法及其運(yùn)行原理,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-07-07
golang gin 框架 異步同步 goroutine 并發(fā)操作
這篇文章主要介紹了golang gin 框架 異步同步 goroutine 并發(fā)操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-12-12

