Go浮点精度丢失是IEEE 754固有缺陷,需通过decimal库、整数运算或json.Number等方案规避;float64除法四舍五入须用math.Round(x*100)/100而非格式化;金融计算严禁先转float64再转decimal。

Go语言中浮点计算精度丢失无法“控制”,只能规避——因为这是 IEEE 754 二进制表示的固有缺陷,不是参数调得细一点就能修好的问题。关键在于:该用什么类型、在哪个环节介入、以及哪些操作会悄悄把误差放大。
float64 除法结果四舍五入到 N 位小数的正确写法
直接 fmt.Printf("%.2f", 10.0/3.0) 只是格式化显示,底层值仍是 3.3333333333333335;若需后续参与运算,必须先做数值级四舍五入。
- 用
math.Round+ 缩放因子:例如保留 2 位小数,应写math.Round(x*100) / 100,而不是math.Round(x*100)/100.0(后者无实质区别,但显式写100更清晰) - 注意:
math.Round对负数也按“远离零”取整,如math.Round(-2.5)得-3,若需银行家舍入(偶数舍入),shopspring/decimal提供RoundBank方法 - 别用
strconv.ParseFloat(fmt.Sprintf("%.2f", x), 64):字符串来回转换不仅慢,还可能因fmt的默认舍入规则引入额外偏差
金融/金额类计算必须绕开 float64
哪怕你只做一次 float64(199.99),精度就已丢失——199.99 在二进制中无法精确表达,实际存的是最接近它的 float64 值。
- 首选方案:用
github.com/shopspring/decimal,从原始字符串或整数构建,例如decimal.RequireFromString("199.99")或decimal.NewFromInt(19999).Div(decimal.NewFromInt(100)) - 绝对不要:先用
strconv.ParseFloat("199.99", 64)得到float64,再传给decimal.NewFromFloat——误差已经进来了,再转 decimal 也没用 - 简单场景可退而求其次:全程用
int64表示“分”,所有运算都整数化,最后除以 100 转显示,但要注意溢出(如超 922 万亿分)
JSON 解析大整数时精度丢失的拦截点
前端发 {"id": 12345678901234567890},Go 默认解析成 float64,此时 12345678901234567890 已变成 12345678901234567168(差 722),无法恢复。
立即学习“go语言免费学习笔记(深入)”;
- 结构体字段声明为
ID json.Number `json:"id"`,然后调.Int64()或.String(),不经过float64中转 - 处理泛型
map[string]interface{}时,解码前必须调decoder.UseNumber(),否则 map 里数字还是float64 - 前端必须配合:超
2^53 - 1的整数一律发字符串,Go 结构体加,string标签,例如ID string `json:"id,string"`
真正容易被忽略的点是:精度问题从来不是单个函数能解决的,它藏在类型选择、数据流转路径、甚至前后端约定里。一个 float64 值一旦生成,后面所有基于它的运算,误差只会累积,不会消失。


















