big.Int算术方法返回*big.Int因其设计为可变值类型,原地修改接收者以避免内存分配;big.Float精度需初始化时显式设置,转换需防溢出,且math/big类型均非并发安全。

big.Int 加减乘除为什么结果是 *big.Int?
因为 big.Int 是值类型但设计为可变(mutable),所有算术方法都原地修改接收者并返回 *big.Int 本身,不是新对象。不这么设计的话,每次运算都要分配内存,性能差且容易误用。
常见错误:写成 a.Add(b, c) 却以为 a 没变,其实 a 已被覆盖;或者链式调用 a.Add(b, c).Mul(d, e) 失败——Mul 不接受 *big.Int 返回值当接收者,得拆开写。
- 正确做法:先初始化目标变量,再复用它:
res := new(big.Int).Add(a, b).Mul(res, c) - 避免意外覆盖:需要保留原值时,显式复制:
copy := new(big.Int).Set(original) - 除法要注意:
Quo是整除,余数丢弃;要余数用QuoRem,它返回两个*big.Int
big.Float 如何控制精度和舍入模式?
big.Float 的精度不是全局设置,而是每个实例独立携带的。没指定时默认 64 位,远低于 float64 实际能表示的精度(约 17 位十进制),容易误以为“高精度”却输出一样。
典型现象:两个 big.Float 相加后调 Text('g', 20),结果还是只显示十几位——因为内部精度不够,补再多格式位也没用。
立即学习“go语言免费学习笔记(深入)”;
- 初始化必须设足够精度:
new(big.Float).SetPrec(256)(单位是二进制位) - 舍入由
SetMode控制,默认big.ToNearestEven;计算中混用不同 mode 可能导致不可预测截断 - 从
float64赋值会带入原始误差,应优先用字符串构造:new(big.Float).SetPrec(256).SetString("3.14159265358979323846")
big.Int 和 big.Float 之间怎么安全转换?
没有直接互转方法,因为整数转浮点可能溢出或丢失精度,浮点转整数需明确取整策略。强行用 Float64() 再塞进 big.Float,等于白用大数库。
常见踩坑:用 big.Float.SetInt 传一个超大 *big.Int,结果变成 +Inf——因为当前 big.Float 精度或指数范围撑不住。
- 整数 → 浮点:先确认值在目标精度下可精确表示,再用
SetInt;否则应走字符串中转 - 浮点 → 整数:用
Int(nil)得到截断后的*big.Int,或Int64()看是否溢出 - 别依赖
Float64():它只返回最接近的float64值,跟原始big.Float可能差几个数量级
为什么 math/big 在并发场景下不能直接共享变量?
big.Int 和 big.Float 都不是线程安全的——它们的方法会修改内部字段(比如 abs 切片、mant 数组),多个 goroutine 同时调 Add 或 Set 会导致数据竞争甚至 panic。
现象包括:计算结果偶尔错、panic: runtime error: slice bounds out of range、或 big.Int.String() 返回空字符串。
- 最简方案:每个 goroutine 自己 new 一个,用完丢掉;复用时用
Set而非直接赋值 - 若需共享状态,加
sync.Mutex或改用不可变风格(每次运算返回新实例,自己封装) - 注意:
big.Rat同样不安全,别以为“有理数就更稳”
SetPrec(512) 写晚了半步,后面全白算。










