【golang】JSON反序列化时数值精度问题

767 阅读2分钟

现象

看看如下程序的运行结果:

func main() {
   input := `{"key1":12.12345678901234567, "key2":12345678901234567891, "key3":"12.12345678901234567", "key4":"12345678901234567891"}`
   var mp map[string]interface{}
   err := json.Unmarshal([]byte(input), &mp)
   if err != nil {
      fmt.Println("err, ", err)
      return
   }

   fmt.Printf("mp: %+v\n", mp)
}

运行结果:

mp: map[key1:12.123456789012346 key2:1.2345678901234567e+19 key3:12.12345678901234567 key4:12345678901234567891]

image.png 结果分析:
可以看到key1key2反序列化后的值存在精度丢失问题

原因

  1. 反序列化时,对于数值类型的key,若没有指定反序列化后的类型,则数值默认会反序列化成float64类型
  2. go语言float64类型对于12.1234567890123456712345678901234567891数压根无法存储

因为以上两点,所以反序列化时可能存在数值精度问题。

如下,float64类型压根不能存储12.1234567890123456712345678901234567891数:

func main() {
   var f1 float64 = 12.12345678901234567
   var f2 float64 = 12345678901234567891
   fmt.Println(f1)
   fmt.Println(f2)
}

运行结果:

12.123456789012346
1.2345678901234567e+19

可以看到存在精度问题

为什么float64无法存储很大的整数或精度很高的浮点数?

参考:【golang】关于float64的精度问题

如何解决

使用json.Number
如下:

func main() {
   input := `{"key1":12.12345678901234567, "key2":12345678901234567891, "key3":"12.12345678901234567", "key4":"12345678901234567891"}`
   
   decoder := json.NewDecoder(bytes.NewBuffer([]byte(input)))
   decoder.UseNumber()

   var mp map[string]interface{}
   err := decoder.Decode(&mp)
   if err != nil {
      fmt.Println("err, ", err)
      return
   }

   fmt.Printf("mp: %+v\n", mp)
}

运行结果:

mp: map[key1:12.12345678901234567 key2:12345678901234567891 key3:12.12345678901234567 key4:12345678901234567891]

可以看到,反序列化后数据没有问题。


原理

json.Number本质是string,反序列化的时候将json的数值转成字符串,而字符串不会有精度丢失问题,所以没有问题。json.Number如下:

package json
// A Number represents a JSON number literal.
type Number string

看看的debug结果:

image.png

总结

  1. go语言的float64类型无法存储很大或精度很高的数值,如:12.1234567890123456712345678901234567891,会存在精度丢失问题。
  2. JSON包反序列化时,若没有指定数值类型的key反序列化后的类型,默认会将数值反序列化成flaot64类型
  3. json.Number本质是string,将json数值转成字符串肯定没有精度问题。
  4. 开发过程中(无论什么语言),对于反序列化场景一定要注意是否存在数值精度问题

参考

www.51cto.com/article/697…