Go JSON中序列化大整數(shù)精度丟失的問(wèn)題分析
在 Go 項(xiàng)目中,我們經(jīng)常使用 map[string]interface{} 來(lái)表示動(dòng)態(tài)字段,或者用 JSON 序列化對(duì)象存入數(shù)據(jù)庫(kù)、發(fā)送到 MQ。然而,當(dāng)存儲(chǔ)或傳輸 大整數(shù)(int64) 時(shí),往往會(huì)出現(xiàn)精度丟失的問(wèn)題。本文通過(guò)一個(gè)示例來(lái)詳細(xì)分析原因,并給出解決方案。
場(chǎng)景示例
假設(shè)我們有一個(gè)動(dòng)態(tài) Extra 字段,用于存儲(chǔ)一些擴(kuò)展信息,其中 oldTask 是一個(gè) int64 大整數(shù):
package main
import (
"encoding/json"
"fmt"
)
func main() {
// 模擬 Extra 字段
extra := make(map[string]interface{})
extra["oldTask"] = int64(7587721483007868979)
fmt.Println("【1】原始寫(xiě)入 int64:")
fmt.Printf("type=%T, value=%v\n\n", extra["oldTask"], extra["oldTask"])
}
輸出:
【1】原始寫(xiě)入 int64:
type=int64, value=7587721483007868979
在 Go 內(nèi)存中,extra["oldTask"] 正確保存了 int64 值。
JSON 序列化
接下來(lái)我們將這個(gè) map 序列化為 JSON 字符串(就像你在 BuildRiskPointsStrategy 中做的):
bytes, _ := json.Marshal(extra)
fmt.Println("【2】json.Marshal 后:")
fmt.Println(string(bytes), "\n")
輸出:
【2】json.Marshal 后:
{"oldTask":7587721483007868979}
Go 的 json.Marshal 對(duì) int64 的處理是安全的,大整數(shù)不會(huì)丟失精度。
JSON 反序列化到 map[string]interface{}
然而問(wèn)題出現(xiàn)了,當(dāng)我們反序列化 JSON 到 map[string]interface{} 時(shí):
var extra2 map[string]interface{}
_ = json.Unmarshal(bytes, &extra2)
fmt.Println("【3】json.Unmarshal 后:")
fmt.Printf("type=%T, value=%v\n\n", extra2["oldTask"], extra2["oldTask"])
輸出:
【3】json.Unmarshal 后:
type=float64, value=7587721483007869000
注意:
- 反序列化后,
extra2["oldTask"]變成了 float64 - 原始的 int64 值
7587721483007868979精度丟失 - 這是 Go 標(biāo)準(zhǔn)庫(kù)
encoding/json的默認(rèn)行為:map[string]interface{}中的數(shù)字全部解析成float64。
再轉(zhuǎn)回 int64
如果我們強(qiáng)行把它轉(zhuǎn)回 int64:
lost := int64(extra2["oldTask"].(float64))
fmt.Println("【4】float64 → int64 后:")
fmt.Println("value =", lost)
輸出:
【4】float64 → int64 后:
value = 7587721483007869000
可以看到,數(shù)字已經(jīng)不再精確,丟失了最后幾位。
對(duì)比差值
fmt.Println("【5】對(duì)比:")
fmt.Println("是否相等:", lost == int64(7587721483007868979))
fmt.Println("差值:", lost-int64(7587721483007868979))
輸出:
【5】對(duì)比:
是否相等: false
差值: 79
- 原始值與反序列化后值不一致
- 這就是“精度丟失”的根本原因
問(wèn)題分析
原因總結(jié):
1.Go 的 encoding/json 對(duì) map[string]interface{} 反序列化時(shí):
- 所有數(shù)字默認(rèn)是
float64 - float64 最大安全整數(shù)范圍是
-2^53 ~ 2^53
2.當(dāng) int64 值超出 2^53 時(shí),float64 表示不精確 → 精度丟失
Go 官方文檔說(shuō)明:JSON numbers without decimal points are decoded as float64 when the target is interface{}。
解決方案
方案 1:使用字符串存儲(chǔ)大整數(shù)
extra["oldTask"] = fmt.Sprintf("%d", oldTaskInt64)
- JSON 序列化后:
"oldTask":"7587721483007868979" - 寫(xiě)入數(shù)據(jù)庫(kù) / MQ / ES 時(shí)不會(huì)丟精度
- 讀取時(shí)再用
strconv.ParseInt轉(zhuǎn)回 int64
方案 2:使用json.Number解析
var extra2 map[string]interface{}
decoder := json.NewDecoder(bytes.NewReader(jsonBytes))
decoder.UseNumber() // 使用 json.Number 代替 float64
decoder.Decode(&extra2)
extra2["oldTask"]類型是json.Number- 可以安全轉(zhuǎn)成
int64或string
num, _ := extra2["oldTask"].(json.Number).Int64()
方案 3:使用 struct 明確類型
type RiskExtra struct {
OldTask int64 `json:"oldTask"`
}
- 避免 map[string]interface{} → float64
- JSON 反序列化會(huì)直接解析為 int64
結(jié)論
Go map[string]interface{} + JSON 是大整數(shù)丟失精度的高發(fā)場(chǎng)景
精度丟失的原因:JSON 默認(rèn)解析數(shù)字為 float64
解決方案:
- 使用字符串存儲(chǔ)大整數(shù)
- 使用
json.Number - 或者盡量使用 struct 明確類型
到此這篇關(guān)于Go JSON中序列化大整數(shù)精度丟失的問(wèn)題分析的文章就介紹到這了,更多相關(guān)Go JSON序列化內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Go用兩個(gè)協(xié)程交替打印100以內(nèi)的奇偶數(shù)的方法詳解
這篇文章主要給大家詳細(xì)介紹了Go用兩個(gè)協(xié)程交替打印100以內(nèi)的奇偶數(shù)的示例代碼,文中給大家介紹了兩個(gè)實(shí)現(xiàn)方法,使用無(wú)緩沖的channel和設(shè)置GOMAXPROCS=1,介紹的非常詳細(xì),需要的朋友可以參考下2023-08-08
Golang內(nèi)存泄漏場(chǎng)景以及解決方案詳析
golang中內(nèi)存泄露的發(fā)現(xiàn)與排查一直是來(lái)是go開(kāi)發(fā)者頭疼的一件事,下面這篇文章主要給大家介紹了關(guān)于Golang內(nèi)存泄漏場(chǎng)景以及解決的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-01-01
2026年Go語(yǔ)言開(kāi)發(fā)中沒(méi)有它就不行的10個(gè)庫(kù)總結(jié)
Go語(yǔ)言以其簡(jiǎn)潔高效、并發(fā)安全、跨平臺(tái)編譯等特性,在后端服務(wù)、云原生、DevOps 工具等領(lǐng)域占據(jù)重要地位,這篇文章主要介紹了2026年Go語(yǔ)言開(kāi)發(fā)中沒(méi)有它就不行的10個(gè)庫(kù),文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2026-05-05
解決go語(yǔ)言ssh客戶端密碼過(guò)期問(wèn)題
這篇文章主要介紹了go語(yǔ)言ssh客戶端解決密碼過(guò)期問(wèn)題,本文給大家分享了解決的方法和原理,非常不錯(cuò),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-04-04
Go語(yǔ)言開(kāi)發(fā)前后端不分離項(xiàng)目詳解
這篇文章主要為大家介紹了Go語(yǔ)言開(kāi)發(fā)前后端不分離項(xiàng)目詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-11-11
go語(yǔ)言版的ip2long函數(shù)實(shí)例
這篇文章主要介紹了go語(yǔ)言版的ip2long函數(shù),實(shí)例分析了Go語(yǔ)言實(shí)現(xiàn)的ip2long函數(shù)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-02-02

