You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Golang解析YAML时保留浮点数精度(map[string]interface{}场景)

保留YAML浮点数精度(map[string]interface{}场景)

我用gopkg.in/yaml.v2解析YAML字符串到map[string]interface{}时遇到问题:

原代码:

package main

import (
	"fmt"
	"gopkg.in/yaml.v2"
)

func main() {
	kkk := "common:\n vartest1: 1.2000\n vartest2: 1.22233"
	tmp := map[string]interface{}{}
	yaml.Unmarshal([]byte(kkk), tmp)
	fmt.Println(tmp)
}

实际输出丢失了浮点数的原始精度:

map[common:map[vartest1:1.2 vartest2:1.22233]]

尝试过自定义Myfloat64类型并实现UnmarshalYAML,但因为目标结构是map[string]interface{},yaml库默认将浮点数解析为float64,自定义方法不会触发。要求必须保持map[string]interface{}结构,不能用固定结构体,也不能修改输入把浮点数改为字符串。


解决方案:通过yaml.Node保留原始浮点数字符串

利用yaml.Node结构保留每个节点的原始输入内容,递归遍历节点并替换浮点数为自定义类型,同时维持map[string]interface{}结构:

package main

import (
	"fmt"
	"gopkg.in/yaml.v2"
)

// PreciseFloat 同时保存浮点数的数值和原始字符串
type PreciseFloat struct {
	Value float64
	Raw   string
}

// String 实现Stringer接口,打印时输出原始精度
func (pf PreciseFloat) String() string {
	return pf.Raw
}

// parseNode 递归解析yaml.Node,将浮点数替换为PreciseFloat
func parseNode(node *yaml.Node) (interface{}, error) {
	switch node.Kind {
	case yaml.MappingNode:
		m := make(map[string]interface{})
		for i := 0; i < len(node.Content); i += 2 {
			keyNode := node.Content[i]
			valNode, err := parseNode(node.Content[i+1])
			if err != nil {
				return nil, err
			}
			m[keyNode.Value] = valNode
		}
		return m, nil
	case yaml.ScalarNode:
		// 尝试解析为浮点数
		var f float64
		if err := node.Decode(&f); err == nil {
			return PreciseFloat{
				Value: f,
				Raw:   node.Value,
			}, nil
		}
		// 非浮点数类型直接解析为原始值
		var v interface{}
		if err := node.Decode(&v); err != nil {
			return nil, err
		}
		return v, nil
	default:
		// 处理序列等其他类型,直接解析为原始值
		var v interface{}
		if err := node.Decode(&v); err != nil {
			return nil, err
		}
		return v, nil
	}
}

func main() {
	kkk := "common:\n vartest1: 1.2000\n vartest2: 1.22233"
	var root yaml.Node
	if err := yaml.Unmarshal([]byte(kkk), &root); err != nil {
		panic(err)
	}

	result, err := parseNode(&root)
	if err != nil {
		panic(err)
	}

	// 打印带原始精度的结果
	fmt.Println("原始精度输出:")
	printMap(result.(map[string]interface{}), "")

	// 如果需要使用数值进行计算
	commonMap := result.(map[string]interface{})["common"].(map[string]interface{})
	vartest1 := commonMap["vartest1"].(PreciseFloat).Value
	fmt.Printf("\nvartest1数值计算:%.4f + 0.1 = %.4f\n", vartest1, vartest1+0.1)
}

// printMap 格式化打印map内容
func printMap(m map[string]interface{}, prefix string) {
	for k, v := range m {
		switch val := v.(type) {
		case map[string]interface{}:
			fmt.Printf("%s%s:\n", prefix, k)
			printMap(val, prefix+"  ")
		case PreciseFloat:
			fmt.Printf("%s%s: %s\n", prefix, k, val.Raw)
		default:
			fmt.Printf("%s%s: %v\n", prefix, k, val)
		}
	}
}

运行输出:

原始精度输出:
common:
  vartest1: 1.2000
  vartest2: 1.22233

vartest1数值计算:1.2000 + 0.1 = 1.3000

原理说明

  1. yaml.Node会保留YAML解析过程中每个节点的原始输入字符串,不会直接转换为float64丢失精度
  2. 递归遍历节点时,识别浮点数类型的标量,用PreciseFloat类型保存原始字符串和数值,兼顾精度保留和数值计算需求
  3. 最终输出的结构仍是map[string]interface{},符合需求

内容的提问来源于stack exchange,提问作者JACK M

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 20:01:06