float64不可用于金融等需十进制精确的场景,因其遵循IEEE 754二进制标准,无法精确表示0.1等十进制小数,导致四舍五入、比较、累加等操作必然失真;应使用shopspring/decimal等十进制高精度库,并严格从字符串初始化、匹配数据库DECIMAL类型、API强制金额传字符串,杜绝任何环节回退float64。

float64不该用于金额、税率、库存数量等任何需要十进制精确表达的场景——这是 Go 工程中精度失控的根源,不是“偶尔出错”,而是“必然失真”。
为什么 float64 在金融/计费类逻辑里根本不可信
IEEE 754 的二进制浮点表示无法精确存储大多数十进制小数。0.1 + 0.2 ≠ 0.3 是表象,真正危险的是:四舍五入、比较、累加、序列化后再反序列化,每一步都在放大误差。
-
math.Round(2.555*100)/100得到2.55而非2.56,因为2.555*100实际是255.49999999999997 -
0.3 == 0.1 + 0.2返回false,导致 if 分支永远不进 - 数据库写入
float64字段后读回再计算,可能因精度截断产生新偏差
用 shopspring/decimal 替代前,必须明确三件事
它不是“更准的 float”,而是完全不同的数据模型:用整数(value)+ 十进制指数(exponent)模拟手算。但误用照样翻车。
- 初始化必须用字符串或
decimal.NewFromInt,decimal.NewFromFloat(0.1)会把原始float64的失真带进来 - 所有运算默认保留被操作数中最少的小数位数,
decimal.NewFromFloat(1.23).Add(decimal.NewFromFloat(4.5))结果是5.7(只保留一位),不是5.73 - JSON 序列化默认输出字符串(如
"123.45"),前端解析时若直接转成 JSNumber,又掉回浮点陷阱
工程落地时绕不开的四个硬约束
精度控制不是加个库就完事,得嵌进整个数据流。
- 数据库字段类型必须匹配:PostgreSQL 用
DECIMAL(p,s),MySQL 用DECIMAL,绝不用FLOAT或DOUBLE - HTTP API 输入校验要拒绝
float类型 JSON 数字,强制要求金额字段为字符串(如{"amount": "199.99"}),在 handler 层立即转decimal.Decimal - 日志打印时避免隐式调用
.String(),防止高并发下大量临时字符串分配;敏感字段建议打脱敏值或用.Floor().String()截断 - 测试必须覆盖边界值:
"0.005"四舍五入、"999999999999.99"溢出、空字符串转decimal等异常路径
关键不在“用没用 decimal”,而在“从用户输入、到 DB、再到下游服务,有没有任何一个环节偷偷把它塞回了 float64”。这种漏点往往藏在中间件、ORM 映射、日志装饰器或监控埋点里。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











