解决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语言开发效率的小技巧实例(GO语言语法糖)汇总

    提升Go语言开发效率的小技巧实例(GO语言语法糖)汇总

    这篇文章主要介绍了提升Go语言开发效率的小技巧汇总,也就是Go语言的语法糖,掌握好这些可以提高我们的开发效率,需要的朋友可以参考下
    2022-11-11
  • 快速解决Golang Map 并发读写安全的问题

    快速解决Golang Map 并发读写安全的问题

    这篇文章主要介绍了快速解决Golang Map 并发读写安全的问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-12-12
  • Go语言TCP从原理到代码实现详解

    Go语言TCP从原理到代码实现详解

    这篇文章主要为大家介绍了Go语言TCP从原理到代码实现详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-08-08
  • 使用Go实现优雅重启服务功能

    使用Go实现优雅重启服务功能

    这篇文章主要介绍了如何使用Go来实现优雅重启服务,本文给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
    2019-11-11
  • Golang数据类型比较详解

    Golang数据类型比较详解

    这篇文章主要围绕Golang数据类型比较详细展开,文中有详细的比较过程,需要的朋友可以参考一下
    2023-04-04
  • Go语言defer语句的三种机制整理

    Go语言defer语句的三种机制整理

    在本篇文章里小编给大家分享的是一篇关于Go语言defer语句的三种机制整理,需要的朋友们学习下吧。
    2020-03-03
  • 构建Golang应用最小Docker镜像的实现

    构建Golang应用最小Docker镜像的实现

    这篇文章主要介绍了构建Golang应用最小Docker镜像的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-05-05
  • golang elasticsearch Client的使用详解

    golang elasticsearch Client的使用详解

    这篇文章主要介绍了golang elasticsearch Client的使用详解,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-05-05
  • Golang中 Slice的分析与使用源码解析

    Golang中 Slice的分析与使用源码解析

    这篇文章主要介绍了Golang 中 Slice的分析与使用(含源码),本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-03-03
  • Go语言中如何实现并发

    Go语言中如何实现并发

    Go的并发机制通过协程和通道的简单性和高效性,使得编写并发代码变得相对容易,这种并发模型被广泛用于构建高性能的网络服务、并行处理任务和其他需要有效利用多核处理器的应用程序,这篇文章主要介绍了在Go中如何实现并发,需要的朋友可以参考下
    2023-09-09

最新评论