Golang JSON 序列化中的 Inf 错误处理及解决方案

文章目录

在 Golang 的 JSON 序列化过程中,遇到除数为零的情况可能会导致返回 Inf(无穷大)。这是 Golang 实现了 IEEE 754 标准的结果,当除数在运行时动态为零时,返回的是 +Inf-Inf,而不是 panic。但这种情况在 JSON 序列化时,Inf 值会触发错误:

json: unsupported value: +Inf

问题分析:Golang 运行时的除零出现 Inf

在 Golang 中,浮点数运算时,如果除数为零,会按照 IEEE 754 标准返回无穷大(+Inf-Inf)。这种行为虽然符合浮点数标准,但在序列化为 JSON 时会导致 Golang 返回错误,特别是当这些 Inf 值包含在结构体中。

例如,在下面的代码中,如果运行时的除数变为零,结果为 +Inf,会导致 JSON 序列化失败。

package main

import (
    "encoding/json"
    "fmt"
    "strconv"
)

func interfaceToFloat64(unk interface{}) (result float64) {
    switch i := unk.(type) {
    case float64:
        result = float64(i)
    case float32:
        result = float64(i)
    case int64:
        result = float64(i)
    case int32:
        result = float64(i)
    case int:
        result = float64(i)
    case uint32:
        result = float64(i)
    case uint64:
        result = float64(i)
    case uint:
        result = float64(i)
    case string:
        result, _ = strconv.ParseFloat(i, 64)
    default:
        result = 0.0
    }
    return
}

func main() {
    var (
        a interface{} = 1
        b interface{} = 0
    )
    x := interfaceToFloat64(a) / interfaceToFloat64(b)
    fmt.Println("x:", x)  // 输出:+Inf

    type T struct {
        F float64
    }
    t := T{F: x}

    _, err := json.Marshal(t)
    fmt.Println("err:", err)  // 输出:json: unsupported value: +Inf
}

解决方案:避免 JSON 序列化时的 Inf

为了防止在序列化时发生 InfNaN 错误,可以在序列化前对浮点数进行检查,检测并处理这些特殊值。

Golang 提供了 math 包中的方法 IsNaNIsInf,可以用于判断浮点数是否为 NaNInf

import (
    "math"
)

// 处理浮点数
if math.IsNaN(x) || math.IsInf(x, 0) {
    x = 0.0  // 或者选择其他值如 null
}

通过这样的检测,可以确保在 JSON 序列化之前,将特殊的 NaNInf 转换为有效值,避免 JSON 序列化失败。

实践中的应用

为了更方便地处理包含浮点数的结构体,可以封装一个通用的函数,将这些结构体在序列化前处理:

package main

import (
    "encoding/json"
    "fmt"
    "math"
)

type T struct {
    F float64
}

func safeMarshal(v interface{}) ([]byte, error) {
    // 遍历结构体中的浮点数并处理
    if t, ok := v.(*T); ok {
        if math.IsNaN(t.F) || math.IsInf(t.F, 0) {
            t.F = 0.0  // 可以根据需要设置默认值
        }
    }
    return json.Marshal(v)
}

func main() {
    x := math.Inf(1)  // +Inf
    t := T{F: x}

    // 使用 safeMarshal 函数进行序列化
    data, err := safeMarshal(&t)
    if err != nil {
        fmt.Println("JSON 序列化错误:", err)
    } else {
        fmt.Println("JSON 序列化结果:", string(data))
    }
}

JSON 序列化的其他考虑

  • NaN 的处理:NaN 是另一种需要特别处理的特殊值。类似 InfNaN 在 JSON 序列化时也会触发错误。因此,建议同时处理这两种情况。
  • API 返回格式:当 API 返回的浮点数计算涉及可能产生 NaNInf 的场景时,最好提前处理这些特殊值,以免引发客户端解析错误。常见的处理方法包括将这些值设置为零或返回 null,具体取决于业务需求。

参考文档

FAQ

  1. 为什么在 Golang 中除数为零会返回 Inf 根据 IEEE 754 标准,浮点数除以零的结果会是 +Inf-Inf,这是浮点数运算的一种定义,而非程序的错误。

  2. 如何处理 JSON 序列化时的 Inf 值? 在 JSON 序列化之前,可以使用 math.IsNaNmath.IsInf 检测并替换这些特殊值,避免序列化失败。

  3. 什么是 NaNInf NaN 代表“非数字”(Not a Number),常见于非法数学操作的结果,如 0/0Inf 则表示无穷大,通常出现在除数为零时。


也可以看看