math/big.Int 不支持算术运算符,必须调用方法如 Add、Quo;需显式初始化,SetString 要指定进制;复用实例需防指针共享;零值为 nil,未初始化调用会 panic。

math/big.Int 不能直接用算术运算符
Go 的 math/big.Int 是引用类型,不支持 +、-、*、/ 这类操作符。写 a + b 会编译失败——这不是语法糖缺失,而是设计上强制你显式选择目标变量和行为(比如是否复用内存)。
常见错误现象:invalid operation: a + b (operator + not defined on *big.Int)
- 所有运算必须调用方法,如
a.Add(a, b)(注意:第一个参数是接收结果的地址,常复用a自身) - 多数方法返回接收者本身(即链式调用可行),但别误以为是“返回新对象”——它就地修改
- 初始化必须用
new(big.Int)或big.NewInt(x);var x *big.Int后直接运算会 panic - 除法
Quo和取模Rem行为与 Go 原生整数一致(向零截断),但DivMod一次算出商和余数更高效
从字符串或 []byte 解析超大整数要指定进制
读取用户输入、JSON 或文件里的大数时,不能用 strconv.ParseInt(溢出),得靠 math/big.Int.SetString。但它默认按 10 进制解析,而十六进制、二进制数据很常见——漏设进制参数会导致静默错误(比如把 "ff" 当 10 进制解析成 111)。
-
SetString("123456789012345678901234567890", 10)正确解析十进制 -
SetString("ff", 16)才得到 255;若传 10,结果是 111 - 二进制用 2,八进制用 8;进制超出 [2, 36] 范围时返回 false
- 字符串开头带
0x或0b不会自动识别——必须手动去掉前缀再指定进制
避免意外的指针共享和内存泄漏
*big.Int 内部用 []big.Word 存大数位,底层是切片。方法如 Add、Mul 会复用接收者的底层数组空间,这提升性能但也埋下坑:多个变量指向同一个 *big.Int 实例时,一次运算可能意外改掉其他地方还在用的值。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 别写
c = a.Add(a, b)然后继续用a——a已被修改 - 需要保留原值?用
c := new(big.Int).Add(a, b)显式新建 - 频繁创建/销毁大数时,可考虑复用
*big.Int实例(如放在 struct 字段或 sync.Pool 里),但必须确保无并发读写 - 注意
big.Int没有深拷贝方法;Set是浅赋值,SetBytes会复制字节,但Bytes()返回的是只读副本
性能敏感场景下慎用 Float、Rat,优先用 Int
如果只是做精确整数运算(比如哈希、ID 计算、密码学中的模幂),永远优先用 *big.Int。用 *big.Float 或 *big.Rat 不仅慢一个数量级,还会引入精度控制复杂度(比如 Float 的精度位数设置、舍入模式)。
-
big.Int运算是任意精度、无误差的整数运算;big.Float是浮点近似,哪怕设高精度也非绝对精确 -
big.Rat虽然精确表示有理数,但分子分母都用*big.Int,内存开销翻倍,且除法后约分成本高 - 密码学库(如
crypto/dsa)内部全用*big.Int,不是没理由的 - 真要算大数开方、对数?先确认是否必须——很多场景可换算法绕过,比如用位运算替代除法
最易被忽略的一点:big.Int 的零值是 nil,不是“0”。任何未初始化的 *big.Int 变量,直接调 String() 或 Int64() 会 panic,而不是返回 "0" 或 0。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










