usenumber 不能直接保留浮点数精度,因为它仅使 json.decoder 将数字解析为 json.number 字符串,对强类型结构体字段(如 float64)无效;需配合 interface{} 或自定义类型实现 unmarshaljson 才能真正避免精度丢失。

为什么 UseNumber 不能直接保留浮点数精度
UseNumber 的作用是让 json.Decoder 把 JSON 数字解析为 json.Number 类型(即字符串),而不是自动转成 float64。它解决的是“整数溢出”和“小数位丢失”的问题,但不是万能精度兜底方案——尤其对科学计数法或超长小数,json.Number 本身只是字符串容器,后续仍需你自己用 strconv.ParseFloat 或高精度库(如 big.Float)处理。
常见误判:以为开启 UseNumber 后再赋值给 float64 字段就能保精度。实际会立刻丢失,因为 float64 本身只有约 15–17 位有效数字。
- JSON 中
"12345678901234567890.123456789"→json.Number可完整存为字符串 - 若结构体字段是
Amount float64,解码时仍会强制转float64,UseNumber对该字段无效 - 必须配合
interface{}或自定义类型 +UnmarshalJSON才能真正跳过默认浮点转换
正确启用 UseNumber 的三步实操
关键不是调用 UseNumber(),而是让它生效的上下文——它只影响后续对 interface{} 或 map[string]interface{} 的解码行为,对强类型结构体字段无作用。
详细的 Three.js 3D 图形参考,涵盖场景设置、相机、几何体、材质、光照、动画、控制器、加载器、数学工具和调试。
- 调用
decoder.UseNumber()必须在Decode()之前,且仅对当前Decoder实例生效 - 解码目标必须是
interface{}或map[string]interface{},否则被忽略 - 拿到
json.Number后,用.String()获取原始字符串,再按需解析(如big.NewFloat().SetPrec(256).Parse(...))
data := []byte(`{"price": 12345678901234567890.123456789}`)
dec := json.NewDecoder(bytes.NewReader(data))
dec.UseNumber() // 必须在这一步设置
var v interface{}
if err := dec.Decode(&v); err != nil {
log.Fatal(err)
}
m := v.(map[string]interface{})
num := m["price"].(json.Number) // 类型断言必须准确
fmt.Println(num.String()) // 输出原始字符串,无精度损失
对接结构体字段:必须实现 UnmarshalJSON
想让某个字段(比如 Price)天然支持高精度,不能依赖 UseNumber 自动适配。Go 的结构体解码走的是字段类型反射路径,UseNumber 不介入这个流程。唯一办法是定义自定义类型并实现 UnmarshalJSON 方法。
- 类型定义要基于
json.Number或string,避免底层用float64 -
UnmarshalJSON内部用json.Unmarshal先转成json.Number,再用big.Float解析 - 注意处理空值、非数字输入等边界情况,否则 panic
type PreciseFloat struct {
*big.Float
}
func (pf *PreciseFloat) UnmarshalJSON(data []byte) error {
var num json.Number
if err := json.Unmarshal(data, &num); err != nil {
return err
}
f, ok := new(big.Float).SetPrec(256).Parse(num.String(), 10)
if !ok {
return fmt.Errorf("invalid number: %s", num.String())
}
pf.Float = f
return nil
}
// 使用
type Order struct {
Price PreciseFloat `json:"price"`
}
容易踩的坑:json.Number 不是数值类型,别直接参与运算
json.Number 是字符串包装器,没有加减乘除方法。常见错误是把它当 float64 直接传给 math 函数或数据库驱动,结果 panic 或静默截断。
- 所有运算前必须显式转为目标数值类型,例如
strconv.ParseFloat(n.String(), 64)(但会丢精度) - 若需计算,优先用
big.Float或decimal类库(如shopspring/decimal) - 写入 JSON 时,
json.Number会被原样输出;但若你用big.Float存储,需自己实现MarshalJSON返回字符串形式 - 注意
json.Number的String()返回不带引号的纯数字字符串,可直接用于高精度解析
最麻烦的地方在于:精度保留不是开个开关就完事,它要求你在解码、存储、计算、序列化四个环节都保持一致的类型策略。漏掉任意一环,前面的努力就白费了。










