文章目录
- extra[“oldTask”] = fmt.Sprintf(“%d”, oldTaskInt64) JSON 序列化后:"oldTask":"7587721483007868979" 写入数据库 / MQ / ES 时不会丢精度 读取时再用 strconv.ParseInt 转回 int64
- var extra2 map[string]interface{} decoder := json.NewDecoder(bytes.NewReader(jsonBytes)) decoder.UseNumber() // 使用 json.Number 代替 float64 decoder.Decode(&extra2) extra2["oldTask"] 类型是 json.Number 可以安全转成 int64 或 string num, _ := extra2[“oldTask”].(json.Number).Int64()
- type RiskExtra struct { OldTask int64 `json:”oldTask”` } 避免 map[string]interface{} → float64 JSON 反序列化会直接解析为 int64
目录
- 场景示例
- JSON 序列化
- JSON 反序列化到 map[string]interface{}
- 再转回 int64
- 对比差值
- 问题分析
- 解决方案
- 方案 1:使用字符串存储大整数
- 方案 2:使用json.Number解析
- 方案 3:使用 struct 明确类型
- 结论
在 Go 项目中,我们经常使用 map[string]interface{} 来表示动态字段,或者用 JSON 序列化对象存入数据库、发送到 MQ。然而,当存储或传输 大整数(int64) 时,往往会出现精度丢失的问题。本文通过一个示例来详细分析原因,并给出解决方案。
假设我们有一个动态 Extra 字段,用于存储一些扩展信息,其中 oldTask 是一个 int64 大整数:
package main
import (
"encoding/json"
"fmt"
)
func main() {
// 模拟 Extra 字段
extra := make(map[string]interface{})
extra["oldTask"] = int64(7587721483007868979)
fmt.Println("【1】原始写入 int64:")
fmt.Printf("type=%T, value=%vnn", extra["oldTask"], extra["oldTask"])
}
输出:
【1】原始写入 int64:
type=int64, value=7587721483007868979
在 Go 内存中,extra["oldTask"] 正确保存了 int64 值。
接下来我们将这个 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 对 int64 的处理是安全的,大整数不会丢失精度。
然而问题出现了,当我们反序列化 JSON 到 map[string]interface{} 时:
var extra2 map[string]interface{}
_ = json.Unmarshal(bytes, &extra2)
fmt.Println("【3】json.Unmarshal 后:")
fmt.Printf("type=%T, value=%vnn", extra2["oldTask"], extra2["oldTask"])
输出:
【3】json.Unmarshal 后:
type=float64, value=7587721483007869000
注意:
- 反序列化后,
extra2["oldTask"]变成了 float64 - 原始的 int64 值
7587721483007868979精度丢失 - 这是 Go 标准库
encoding/json的默认行为:map[string]interface{}中的数字全部解析成float64。
如果我们强行把它转回 int64:
lost := int64(extra2["oldTask"].(float64))
fmt.Println("【4】float64 → int64 后:")
fmt.Println("value =", lost)
输出:
【4】float64 → int64 后:
value = 7587721483007869000
可以看到,数字已经不再精确,丢失了最后几位。
fmt.Println("【5】对比:")
fmt.Println("是否相等:", lost == int64(7587721483007868979))
fmt.Println("差值:", lost-int64(7587721483007868979))
输出:
【5】对比:
是否相等: false
差值: 79
- 原始值与反序列化后值不一致
- 这就是“精度丢失”的根本原因
原因总结:
1.Go 的 encoding/json 对 map[string]interface{} 反序列化时:
- 所有数字默认是
float64 - float64 最大安全整数范围是
-2^53 ~ 2^53
2.当 int64 值超出 2^53 时,float64 表示不精确 → 精度丢失
Go 官方文档说明:JSON numbers without decimal points are decoded as float64 when the target is interface{}。
extra["oldTask"] = fmt.Sprintf("%d", oldTaskInt64)
- JSON 序列化后:
"oldTask":"7587721483007868979"
- 写入数据库 / MQ / ES 时不会丢精度
- 读取时再用
strconv.ParseInt 转回 int64
"oldTask":"7587721483007868979"strconv.ParseInt 转回 int64
var extra2 map[string]interface{}
decoder := json.NewDecoder(bytes.NewReader(jsonBytes))
decoder.UseNumber() // 使用 json.Number 代替 float64
decoder.Decode(&extra2)
extra2["oldTask"] 类型是 json.Number
- 可以安全转成
int64 或 string
num, _ := extra2["oldTask"].(json.Number).Int64()
extra2["oldTask"] 类型是 json.Numberint64 或 string
type RiskExtra struct {
OldTask int64 `json:"oldTask"`
}
- 避免 map[string]interface{} → float64
- JSON 反序列化会直接解析为 int64
Go map[string]interface{} + JSON 是大整数丢失精度的高发场景
精度丢失的原因:JSON 默认解析数字为 float64
解决方案:
- 使用字符串存储大整数
- 使用
json.Number - 或者尽量使用 struct 明确类型
到此这篇关于Go JSON中序列化大整数精度丢失的问题分析的文章就介绍到这了,更多相关Go JSON序列化内容请搜索风君子博客以前的文章或继续浏览下面的相关文章希望大家以后多多支持风君子博客!
您可能感兴趣的文章:
- Go语言中json序列化优化技巧
- Golang中自定义json序列化时间格式的示例代码
- golang JSON序列化和反序列化示例详解
- 解决Go Json Unmarshal反序列化丢失数字精度问题
- Go语言对JSON数据进行序列化和反序列化
- golang如何自定义json序列化应用详解
- golang中json反序列化可能遇到的问题