go 的 math/big 不支持金融级高精度浮点运算,因其仅提供 big.int 和 big.rat,缺乏符合十进制语义的 *big.float;推荐使用 shopspring/decimal 库,它基于整数模拟十进制定点数,支持可控舍入、精确比较与安全序列化。

Go 的 math/big 不支持高精度浮点运算 —— 它只有 *big.Int 和 *big.Rat,没有 *big.Float 的官方实现(标准库中不存在)。
为什么不能直接用 math/big 做金融浮点计算
math/big 的设计目标是任意精度整数和有理数(分数),不是小数。金融场景需要的是「定点小数」或「可控舍入的十进制浮点」,比如 19.99 × 100 = 1999 分,而不是 19.990000000000002 这类二进制浮点误差。
-
*big.Rat能精确表示 1999/100,但一旦涉及开方、对数、sin 等非有理运算,就无法保持精度,且 API 不面向货币场景 - 标准库中
*big.Float是实验性类型(math/big包里确实有个Float,但它是为科学计算设计的二进制浮点,不满足金融的十进制语义和 IEEE 754-2008 decimal 规范) - 直接用
float64做钱的加减乘除,在 1e16 以上就会丢精度,比如10000000000000001.0 + 1.0 == 10000000000000000.0
金融计算该用什么:推荐 shopspring/decimal
这是 Go 生态中最成熟、被 Stripe、Coinbase 等公司实际用于生产环境的十进制定点数库,底层用整数模拟十进制小数,所有运算可配置舍入模式(如 RoundHalfUp),完全规避二进制浮点缺陷。
- 初始化金额统一转成「最小单位整数」再包装:比如
decimal.NewFromInt(1999)表示 19.99 元(精度设为 2) - 乘法必须显式指定精度:
d.Mul(decimal.NewFromInt(1999)).Mul(decimal.NewFromInt(105)).Div(decimal.NewFromInt(100))表示打 105% 折,避免中间结果溢出 - 比较必须用
.Equal()/.GreaterThan(),不能用==—— 因为decimal.Decimal是 struct,含精度字段,直接比较会错判 - JSON 序列化默认输出字符串(如
"19.99"),避免前端解析成float64再次失真
如果硬要用 math/big 模拟,怎么绕?
仅限极简场景(比如只做加减、固定两位小数),可用 *big.Int 存「分」,自己管理小数点位置。
立即学习“go语言免费学习笔记(深入)”;
- 输入字符串
"19.99"→ 拆成整数部分和小数部分 →new(big.Int).Mul(..., big.NewInt(100)).Add(...)得到分值 1999 - 所有运算在
*big.Int上进行,最后除以 100 输出时补小数点(注意:除法要自己实现四舍五入,/是截断) - 不能处理
"0.001"这种三位小数,除非统一升位到千分位(即乘 1000),否则精度模型就崩了 - 没有现成的
.Round()或.Abs(),每个操作都要手写逻辑,容易漏掉边界(比如负数舍入规则)
真正麻烦的不是“怎么算”,而是“谁来定义舍入规则”“幂等性如何保证”“数据库字段类型是否匹配”。shopspring/decimal 把这些都收口了;自己用 big.Int 模拟,等于把金融合规风险写进了业务代码里。










