int64运算溢出会静默回绕,float64精度不足,唯一可靠方案是math/big.Int:需用Add/Mul等方法而非运算符,SetString进制须显式指定,JSON反序列化需自定义实现。

为什么 int64 算着算着就变负数了?
Go 默认整型有溢出风险,一旦超过 math.MaxInt64(约 9.2e18),加减乘就会静默回绕,不报错也不提示。比如计算 100! 或解析区块链里的大额 token 余额时,int64 直接失效。
- 溢出不是 panic,是“算对了但结果错了”——你很难第一时间发现
-
float64看似能装更大数,但精度只有 53 位,17 位十进制有效数字之后就开始丢数,比如1 在 float64 下为 true -
math/big.Int是唯一靠谱选择:任意精度、无溢出、支持符号、底层用 uint 数组存值
big.Int 不能直接用 + *,必须调方法
Go 不支持运算符重载,所以 big.Int 所有算术都得靠方法链。常见写法错误是写成 a + b,实际会编译失败:
- 必须用
a.Add(a, b)、c.Mul(a, b)这类原地操作(第一个参数是接收结果的变量) - 想链式计算?得反复传参:
tmp := new(big.Int).Mul(a, b).Add(tmp, c) - 别用
new(big.Int)后直接赋值,比如z := new(big.Int); z = big.NewInt(42)—— 前面的z就泄漏了,应统一用big.NewInt()或复用对象 - 注意方法返回的是 *big.Int 指针,且多数是原地修改,别误以为返回了新对象
从字符串或 hex 解析大数时,进制不写默认是 10,不是 16
读取 JSON、ABI 参数或十六进制哈希时,容易想当然用 big.NewInt(0).SetString("0xabc", 0),结果返回 nil——因为 SetString 的第二个参数是进制,0 表示“自动推断”,但它只认 "0x" 前缀,不认 "0X" 或带空格的字符串。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 十六进制必须显式传
16:z.SetString("abc", 16)或z.SetString("0xabc", 0)(仅小写 0x) - 十进制字符串带前导零?
"00123"会被当八进制解析(如果进制为 0),务必指定10 - 解析失败不 panic,只返回
false,必须检查:if _, ok := z.SetString(s, 10); !ok { /<em> 处理错误 </em>/ }
JSON 反序列化 big.Int 需要自定义 UnmarshalJSON
标准库不支持直接 json.Unmarshal([]byte(<code>{"n":"12345678901234567890"}), &v) 到 <em>big.Int</em>,会报错 “cannot unmarshal string into Go struct field … of type big.Int”。
立即学习“go语言免费学习笔记(深入)”;
- 必须给结构体字段定义自定义方法,或用封装类型:
type BigInt struct { *big.Int } func (b *BigInt) UnmarshalJSON(data []byte) error { s := strings.Trim(string(data), `"`) z := new(big.Int) _, ok := z.SetString(s, 10) if !ok { return errors.New("invalid bigint string") } b.Int = z return nil } - 别忘了导出字段名首字母大写,否则 JSON 包看不到
- 如果只是临时解析,更轻量的做法是先解到
string,再手动SetString
大数不是“换个类型就行”,关键在所有输入输出路径都要对齐:解析、计算、序列化、比较,漏一环就可能在某个边界 case 里突然崩掉。尤其注意 SetString 的进制逻辑和 JSON 反序列化这种“看似应该支持实则要手撸”的地方。

















