float64不可用于金额计算,因其IEEE 754二进制浮点固有缺陷导致精度丢失;应始终用decimal.NewFromString("0.1")等字符串初始化,避免NewFromFloat,并确保数据库、JSON全程直连decimal.Decimal。

金融、计费、对账等场景下,float64 无法安全用于金额计算——这不是 Go 的 bug,而是 IEEE 754 二进制浮点表示的固有缺陷。0.1 + 0.2 == 0.30000000000000004 这类结果不是偶然,而是必然;一旦参与数据库写入、多次累加或等值判断,误差会立刻暴露。
为什么 decimal.NewFromFloat 是最危险的初始化方式
它把一个已经失真的 float64 值“固化”进 decimal.Decimal,看似转高精度,实则把误差打包封装。例如 decimal.NewFromFloat(0.1) 得到的是 0.10000000000000000555...,后续所有运算都基于这个错误起点。
- ✅ 安全做法:用
decimal.NewFromString("0.1")或decimal.New(1, -1)(1 × 10⁻¹) - ✅ 若上游只能给
float64(如 JSON 解析后),先fmt.Sprintf("%.2f", f)格式化为字符串,再传给NewFromString - ❌ 避免链式调用:
decimal.NewFromFloat(a + b)—— 加法已在float64层出错,再包一层无意义 - ⚠️ 注意:
decimal.RequireFromString会 panic,生产环境应改用decimal.NewFromString并检查返回的err
decimal.Decimal 四则运算必须用方法,不能用操作符
decimal.Decimal 是不可变类型,所有运算返回新实例,原值不变;且不支持 ==、 等操作符比较。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
- ✅ 加减:用
a.Add(b)、a.Sub(b),别写a + b - ✅ 乘除:用
a.Mul(b)、a.Div(b);若需控制小数位,优先用a.MulRound(b, 2)而非先乘再Round() - ✅ 比较:用
a.Cmp(b) == 0判断相等,a.Cmp(b) > 0表示a > b - ❌ 常见错误:
if a == b永远为 false;a.Add(b)后不接收返回值,误以为a已被修改
数据库与 JSON 交互时精度断层最常发生在这三处
即便代码层用了 decimal.Decimal,只要中间经过 float64 或默认序列化,精度就丢了。
立即学习“go语言免费学习笔记(深入)”;
- ✅ GORM 模型字段必须声明为
decimal.Decimal类型,不能是float64或string;v2 起需显式注册Scanner和Valuer接口 - ✅ PostgreSQL/MySQL 的
DECIMAL字段,Go 层必须直连decimal.Decimal,避免 ORM 自动转成float64 - ✅ JSON 序列化默认输出字符串(如
"19.99"),若前端要求数字类型,需自定义json.Marshaler,或统一约定后端返回字符串、前端解析 - ⚠️ 日志调试时避免高频调
d.String(),它比fmt.Printf("%s", d)慢一个数量级;批量取整优先用d.Round(2),别绕路做除法
真正难的不是调用哪个方法,而是从输入源头(JSON、表单、DB 查询结果)开始就拒绝 float64 中转——哪怕只经过一次 ParseFloat,精度就不可逆地丢失了。

















