解决Go Json Unmarshal反序列化丢失数字精度问题

 更新时间:2023年08月08日 14:22:53   作者: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内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Go语言字符串及strings和strconv包使用实例

    Go语言字符串及strings和strconv包使用实例

    字符串是工作中最常用的,值得我们专门的练习一下,下面这篇文章主要给大家介绍了关于Go语言字符串及strings和strconv包使用的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2024-06-06
  • 使用Go语言启动Redis的实例详解

    使用Go语言启动Redis的实例详解

    这篇文章主要为大家介绍了Go语言中一个可以用来启动 redis-server 的开源库 github.com/stvp/tempredis,感兴趣的小伙伴可以跟随小编一起学习一下
    2025-01-01
  • Golang channel为什么不会阻塞的原因详解

    Golang channel为什么不会阻塞的原因详解

    这篇文章主要为大家介绍了Golang channel为什么不会阻塞的原因详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-07-07
  • 详解Golang中创建error的方式总结与应用场景

    详解Golang中创建error的方式总结与应用场景

    Golang中创建error的方式包括errors.New、fmt.Errorf、自定义实现了error接口的类型等,本文主要为大家介绍了这些方式的具体应用场景,需要的可以参考一下
    2023-07-07
  • Go语言正则表达式示例

    Go语言正则表达式示例

    这篇文章主要介绍了Go语言正则表达式,结合实例形式分析了Go语言正则表达式实现字符串的匹配、查找等相关操作技巧,需要的朋友可以参考下
    2017-01-01
  • golang gc的内部优化详细介绍

    golang gc的内部优化详细介绍

    Go编译器在垃圾回收(GC)的扫描标记阶段,对存储无指针键值对的map进行了优化,即在GC扫描时不深入扫描map内部数据,只检查map本身是否需要回收,这一优化显著提升了GC扫描的速度,从而减少了GC对程序性能的影响
    2024-10-10
  • Golang全局变量加锁的问题解决

    Golang全局变量加锁的问题解决

    这篇文章主要介绍了解决Golang全局变量加锁的问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-05-05
  • Go语言字符串操作指南:简单易懂的实战技巧

    Go语言字符串操作指南:简单易懂的实战技巧

    本文将介绍Go语言中字符串的实战操作,通过本文的学习,读者将掌握Go语言中字符串的常用操作,为实际开发提供帮助,需要的朋友可以参考下
    2023-10-10
  • Golang学习笔记(一):简介

    Golang学习笔记(一):简介

    这篇文章主要介绍了Golang学习笔记(一):简介,本文讲解了Go语言最主要的特性、安装、环境变量设置、整体目录结构、Helloworld、go命令、调试、编辑器设置等内容,需要的朋友可以参考下
    2015-05-05
  • go常用指令之go mod详解

    go常用指令之go mod详解

    当go命令运行时,它查找当前目录然后查找相继的父目录来找出 go.mod,下面这篇文章主要给大家介绍了关于go常用指令之go mod的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-08-08

最新评论