math 包不支持高精度计算,仅处理 float64,受 ieee 754 限制,存在舍入误差;math.pow(10,18)+1==math.pow(10,18) 因 float64 有效位约 15–17 位,超 1e16 后相邻数间隔 ≥1;big.int 超 int64 需用 setstring 解析字符串,除法为向零截断。

math 包不提供高精度计算能力,它只处理 float64(或少数函数支持 float32),所有运算都受 IEEE 754 限制,存在舍入误差、溢出、NaN 和 Inf 等浮点行为。真要高精度,必须用 math/big。
为什么 math.Pow(10, 18) + 1 == math.Pow(10, 18)?
因为 float64 只有约 15–17 位十进制有效数字。当数值超过 1e16 后,相邻可表示数的间隔 ≥ 1,+1 就“消失”了:
-
math.Pow(10, 18)是1e18,其下一个可表示float64是1e18 + 256(实际间隔由尾数位决定) - 所以
1e18 + 1会被就近舍入回1e18 - 这不是 bug,是 IEEE 754 的固有约束;
math包所有函数都运行在这个模型上
big.Int 初始化时踩坑:new(big.Int).SetInt64(9223372036854775808) 会截断
big.NewInt 和 (*Int).SetInt64 都只接受 int64 参数,超限值直接溢出(如 9223372036854775808 → -9223372036854775808)。正确做法是走字符串解析:
Go语言(Golang)1.26.0版本提供 Go 官方 Windows amd64 MSI 安装包下载入口,版本号 1.26.0,可用于旧项目维护、兼容性测试和指定版本开发环境配置。
- 用
new(big.Int).SetString("9223372036854775808", 10),第二个返回值ok必须检查 - 十六进制字符串要去掉
"0x"前缀:i.SetString("ff", 16)✅,i.SetString("0xff", 16)❌ - 首尾空格会导致静默失败,务必先
strings.TrimSpace(s)
big.Int 除法与原生 / 的行为差异:-7 / 3 在 Go 中是 -2,但 big.Int.Div(-7, 3) 也是 -2 —— 可它不是“向下取整”
big.Int.Div 实现的是向零截断(truncated division),和 Go 原生整数除法一致,但和数学中欧几里得除法不同:
-
Div(a, b)返回商 q 满足a = q*b + r,且|r| ,符号与 <code>a相同 -
QuoRem(a, b)一次性返回商和余数;若要非负余数(如密码学常用),需手动调整:r := new(big.Int).Mod(a, b); if r.Sign() == -1 { r.Add(r, b) } -
Div遇到b == 0会 panic,没有 error 返回,调用前必须用b.Sign() != 0显式检查
性能陷阱:循环里 new(big.Int) 比复用 tmp.Set() 慢 3–5 倍
*big.Int 是指针类型,每次 new(big.Int) 或 big.NewInt(0) 都触发堆分配。高频场景(如哈希计算、大数迭代)下 GC 压力明显:
- 把临时变量声明为局部变量:
var tmp big.Int,然后用tmp.SetInt64(0)或tmp.Set(nil)重置 - 若需并发安全,用
sync.Pool缓存*big.Int,但注意big.Int内部缓冲区不自动清零,每次取出后仍需.Set(nil) -
big.Float同理,SetPrec()调用开销不小,精度应尽早固定,避免反复设置
math 是错的工具,math/big 才是唯一标准答案——但它不是“更精确的 float”,而是完全不同的整数/有理数/浮点数抽象,所有操作语义、内存模型、错误模式都得重学。golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










