现象
看看如下程序的运行结果:
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]
结果分析:
可以看到key1
和key2
反序列化后的值存在精度丢失问题。
原因
- 反序列化时,对于数值类型的key,若没有指定反序列化后的类型,则数值默认会反序列化成
float64
类型 - go语言
float64
类型对于12.12345678901234567
和12345678901234567891
数压根无法存储
因为以上两点,所以反序列化时可能存在数值精度问题。
如下,float64
类型压根不能存储12.12345678901234567
和12345678901234567891
数:
func main() {
var f1 float64 = 12.12345678901234567
var f2 float64 = 12345678901234567891
fmt.Println(f1)
fmt.Println(f2)
}
运行结果:
12.123456789012346
1.2345678901234567e+19
可以看到存在精度问题
为什么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结果:
总结
- go语言的
float64
类型无法存储很大或精度很高的数值,如:12.12345678901234567
和12345678901234567891
,会存在精度丢失问题。 - JSON包反序列化时,若没有指定数值类型的key反序列化后的类型,默认会将数值反序列化成
flaot64
类型 json.Number
本质是string
,将json数值转成字符串肯定没有精度问题。- 开发过程中(无论什么语言),对于反序列化场景一定要注意是否存在数值精度问题