【Golang】Json 无法表示 float64 类型的 NaN 以及 Inf 导致的 panic
【Golang】Json 无法表示 float64 类型的 NaN 以及 Inf 导致的 panic
原因
golang 服务出现了 panic,根据 panic 打印出的堆栈找到了问题代码,看上去原因是:json 序列化时,遇到了无法序列化的内容
[panic]: json: unsupported value: NaN or Infinite

NaN 以及 Infinite
解释:基本可以判断出:NaN 以及 Inf 是 float64 类型的两种特例,Json 无法表示这类数据,故 panic
深度剖析
查阅 log 看到,这里最原始的 NaN 其实是字符串"NaN",明明是字符串,是如何将 "NaN"转变为 float64 的呢?问题出在使用的 cast 包的 ToFloat64上

可以从 ToFloat64 的源码中看到,当需要转换成 float64 的类型是 string 或者 json.Number 时,调用的都是 strconv.ParseFloat 函数(s.Float64 本质也是调用该函数),继续阅读 strconv.ParseFloat,我们可以在strconv/atof.go文件中看到以下代码:strconv.ParseFloat 会将字符串 NaN 以及 Inf 转换为 float64 类型的 NaN 以及 Inf。 而 json 无法处理这两种数据,会直接 panic
修复
单独判断下即可
func SetValWhenFloatIsNaNOrInf(val float64) float64 {
if math.IsNaN(val) {
return 0.00
}
if math.IsInf(val, 0) {
return 100.00
}
return val
}
扩展
NaN 和 Inf 怎么来的呢

在 float64 类型中,我们可以通过 zero/zero 来得到 NaN,也可以用过 除零 操作来得到 Inf,在 Google 并没有得到能解释这两种常量存在的原因,只从二进制浮点数算术标准(IEEE 754)看到有相关的定义
能否把 NaN 以及 Inf 作为 map 的 key?
测试代码
func TestNaNKeyMap() {
m := make(map[float64]struct{}, 0)
for i := 0; i < 10; i++ {
m[math.NaN()] = struct{}{}
fmt.Printf("nan map len:%d\n", len(m))
}
}
func TestInfKeyMap() {
m := make(map[float64]struct{}, 0)
for i := 0; i < 10; i++ {
m[math.Inf(0)] = struct{}{}
fmt.Printf("inf map len:%d\n", len(m))
}
}
结果:可以看待对于 NaN,每次赋值的时候,其实都是给不同的 key 赋值,而 Inf 则不是;所以我们可以得出以下结论:map[float64]struct 这种以 float64 为 key 的 map,存在内存泄漏的可能

map 的 key 都会经过 hash,然后再确定value 存储的位置,那么问题大概率出在 hash 算法上,在 runtime/alg.go 找到以下函数:
可以看到,算法里判断到 f != f 时,会给hash 值增加一个随机数,并且注释里也说了是为了适配 any kind of NaN
这里 f != f 的判断也同时用在 func IsNaN(f float64) (is bool) 函数中。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!