Go的math/big不是更高精度的float64,而是无IEEE 754误差的独立数值模型;big.NewInt(12345678901234567890)因超int64上限被静默截断,须用new(big.Int).SetString("...", 10)并检查ok值。

Go 的 math/big 不是“更高精度的 float64”,而是完全不同的数值模型:它不依赖二进制浮点,因此没有 IEEE 754 舍入误差;但它的“精度”必须显式声明、手动管理,否则结果可能看似正确实则无效。
big.NewInt(12345678901234567890) 为什么错?
因为 big.NewInt 只接受 int64 参数,而字面量 12345678901234567890 在编译期就被 Go 当作整数常量处理——它远超 int64 上限(9223372036854775807),会静默截断成一个完全错误的值。
- 错误写法:
big.NewInt(12345678901234567890)→ 实际传入的是截断后的int64 - 正确写法:
new(big.Int).SetString("12345678901234567890", 10),并检查返回的ok值 - 所有来自用户输入、JSON、配置文件的大数,一律走
SetString,别用strconv.ParseInt中转 -
SetString对空格和前缀极其敏感:" 123 "、"0xff"都会失败;安全做法是先strings.TrimSpace再strings.TrimPrefix
big.Float 的 SetPrec(10) 是什么单位?
SetPrec 的单位是**二进制位(bits)**,不是十进制小数位。设成 10 意味着最多约 3 位有效十进制数字(因为 log₁₀(2¹⁰) ≈ 3.01),远低于常见业务需求。
- 默认精度是
0,行为未定义,必须显式调用SetPrec - 模拟
float64精度:用SetPrec(53)(IEEE 754 double 的尾数位) - 金融或高精度场景建议
SetPrec(256)或更高 -
Text('g', 20)的20是“最大有效数字位数”,不是小数位;要固定小数位,得先用Round或外部四舍五入逻辑 - 从
float64赋值(如SetFloat64(0.1))会带入原始二进制误差,应优先用字符串:SetString("0.1", 10)
big.Int.Add(a, b) 为什么 a 就变了?
*big.Int 所有算术方法(Add、Mul、Div 等)都是就地修改,第一个参数是目标变量,不是“返回新对象”。这设计是为了避免频繁堆分配,但极易误用。
-
a.Add(a, b)→ 把a + b写回a,原a值丢失 - 想保留原值?必须显式拷贝:
copy := new(big.Int).Set(a) - 链式调用不可靠:
a.Add(b, c).Mul(d, e)会失败,因为Mul不接受*big.Int返回值当接收者 - 高频循环中,反复
new(big.Int)比复用一个实例慢 3–5 倍;推荐预分配:tmp := new(big.Int),然后循环内用tmp.SetInt64(0)或tmp.Set(nil)重置
big.Int 和 big.Float 之间能直接转换吗?
不能。二者语义不同:整数无精度损失,浮点数有精度与指数范围限制,强行互转极易溢出或静默失真。
- 整数 → 浮点:先确认值在目标精度下可精确表示,再用
SetInt;否则应走字符串中转 - 浮点 → 整数:用
Int(nil)得到截断后的*big.Int,或Int64()看是否溢出 - 别依赖
Float64():它只返回最接近的float64值,跟原始big.Float可能差几个数量级 - 并发场景下,
*big.Int和*big.Float都不是线程安全的——多个 goroutine 同时调Add或Set会导致数据竞争甚至 panic
最易被忽略的一点:精度不是设置一次就万事大吉。每次参与运算的 big.Float 实例都必须独立携带足够精度,且 SetString 解析失败时只返回 false,不抛异常,不报错信息——漏掉 ok 检查,程序就 quietly wrong。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











