Go JSON中序列化大整数精度丢失的问题分析

 更新时间:2026年01月04日 09:18:24   作者:Kevin666  
当存储或传输 大整数(int64) 时,往往会出现精度丢失的问题,本文通过一个示例来详细分析原因,并给出解决方案,文中的示例代码讲解详细,感兴趣的小伙伴可以了解下

在 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=%v\n\n", extra["oldTask"], extra["oldTask"])
}

输出:

【1】原始写入 int64:
type=int64, value=7587721483007868979

在 Go 内存中,extra["oldTask"] 正确保存了 int64 值。

JSON 序列化

接下来我们将这个 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{}

然而问题出现了,当我们反序列化 JSON 到 map[string]interface{} 时:

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 标准库 encoding/json 的默认行为:map[string]interface{} 中的数字全部解析成 float64

再转回 int64

如果我们强行把它转回 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/jsonmap[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{}

解决方案

方案 1:使用字符串存储大整数

extra["oldTask"] = fmt.Sprintf("%d", oldTaskInt64)
  • JSON 序列化后:"oldTask":"7587721483007868979"
  • 写入数据库 / MQ / ES 时不会丢精度
  • 读取时再用 strconv.ParseInt 转回 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
  • 可以安全转成 int64string
num, _ := extra2["oldTask"].(json.Number).Int64()

方案 3:使用 struct 明确类型

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用两个协程交替打印100以内的奇偶数的方法详解

    Go用两个协程交替打印100以内的奇偶数的方法详解

    这篇文章主要给大家详细介绍了Go用两个协程交替打印100以内的奇偶数的示例代码,文中给大家介绍了两个实现方法,使用无缓冲的channel和设置GOMAXPROCS=1,介绍的非常详细,需要的朋友可以参考下
    2023-08-08
  • 从生成CRD到编写自定义控制器教程示例

    从生成CRD到编写自定义控制器教程示例

    这篇文章主要为大家介绍了从生成CRD到编写自定义控制器的教程示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-05-05
  • Golang内存泄漏场景以及解决方案详析

    Golang内存泄漏场景以及解决方案详析

    golang中内存泄露的发现与排查一直是来是go开发者头疼的一件事,下面这篇文章主要给大家介绍了关于Golang内存泄漏场景以及解决的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2023-01-01
  • 一文掌握Golang模糊测试

    一文掌握Golang模糊测试

    本文主要介绍了一文掌握Golang模糊测试,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-06-06
  • Go官方限流器的用法详解

    Go官方限流器的用法详解

    限流器是提升服务稳定性的非常重要的组件,本文主要介绍了Go官方限流器的用法,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-05-05
  • 2026年Go语言开发中没有它就不行的10个库总结

    2026年Go语言开发中没有它就不行的10个库总结

    Go语言以其简洁高效、并发安全、跨平台编译等特性,在后端服务、云原生、DevOps 工具等领域占据重要地位,这篇文章主要介绍了2026年Go语言开发中没有它就不行的10个库,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2026-05-05
  • 解决go语言ssh客户端密码过期问题

    解决go语言ssh客户端密码过期问题

    这篇文章主要介绍了go语言ssh客户端解决密码过期问题,本文给大家分享了解决的方法和原理,非常不错,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-04-04
  • Go语言开发前后端不分离项目详解

    Go语言开发前后端不分离项目详解

    这篇文章主要为大家介绍了Go语言开发前后端不分离项目详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-11-11
  • Go语言读取文本文件的三种方式总结

    Go语言读取文本文件的三种方式总结

    工作中时不时需要读取文本,文本文件是最常见的文件类型。本文将利用Go语言从逐行、逐个单词和逐个字符三个方法读取文件,感兴趣的可以了解一下
    2023-01-01
  • go语言版的ip2long函数实例

    go语言版的ip2long函数实例

    这篇文章主要介绍了go语言版的ip2long函数,实例分析了Go语言实现的ip2long函数技巧,具有一定参考借鉴价值,需要的朋友可以参考下
    2015-02-02

最新评论