Go中没有内置高精度浮点类型,float64因IEEE-754二进制表示存在固有误差;需精确运算时应选用math/big.Rat或shopspring/decimal等外部方案,并依据业务需求权衡精度与性能。

float64 不是“精密”的浮点数,Go 里没有内置的高精度浮点类型——这是必须先厘清的事实。所谓“精密运算”,实际依赖外部库或手动策略,而非语言原生能力。
为什么 float64 无法满足精密需求
Go 的 float64 遵循 IEEE-754 双精度标准,本质是二进制近似表示。像 0.1 + 0.2 得到 0.30000000000000004 这类误差不是 bug,而是设计使然。
金融计算、科学模拟或测试断言中若直接用 == 比较 float64,大概率出错。
用 math/big.Rat 做精确有理数运算
当需要绝对精度(如分数运算、货币中间计算),math/big.Rat 是 Go 标准库唯一靠谱选择。它把数字存为分子/分母两个大整数,规避小数表示缺陷。
-
Rat构造必须用new(big.Rat).SetFloat64(x)或new(big.Rat).SetFrac(&big.Int{}, &big.Int{}),不能直接赋值小数常量 - 所有运算(
Add、Mul、Quo)都需显式调用方法,返回接收者本身(链式调用可行) - 输出时用
Rat.Float64()会丢失精度;应优先用Rat.FloatString(precision)控制位数,或转成字符串再解析
第三方库选型:shopspring/decimal vs ericlagergren/decimal
业务系统中最常用的是 shopspring/decimal,它专为十进制金融计算设计:
- 初始化用
decimal.NewFromFloat(123.45),内部以整数+小数位数存储,无二进制误差 - 比较必须用
.Equal()、.GreaterThanOrEqual(),不能用== - 除法需指定精度和舍入模式(如
decimal.ROUND_HALF_UP),否则 panic
而 ericlagergren/decimal 更底层、支持更多 IEEE-754 模式,但 API 更复杂,日常开发极少需要。
立即学习“go语言免费学习笔记(深入)”;
何时该放弃“精密”,转而接受可控误差
不是所有场景都需要精确到末位。物理仿真、机器学习梯度计算等,float64 加容差比较(math.Abs(a-b) < 1e-9)更合理且高效。
- 避免在循环中累积
float64误差:改用 Kahan 求和(github.com/leanovate/goma/math/kahan等小工具) - 测试中不要写
assert.Equal(t, 0.3, a+b),改用assert.InDelta(t, 0.3, a+b, 1e-12) - JSON 序列化
float64时,注意encoding/json默认不保留尾随零——若需展示精度,得转成字符串或自定义 marshaler
真正难的不是选哪个库,而是判断问题本质:你要的“精密”是数学意义上的精确,还是业务上可接受的误差范围。这点没想清楚,换十种类型也白搭。


















