big.Int 必须用 Cmp 方法比较大小,因 Go 不支持运算符重载且 *big.Int 是指针,直接用 < 比较的是地址而非数值;Cmp 返回 -1/0/1,推荐用 < 0 等简洁写法,并确保 SetString 解析成功后再比较。

big.Int 没有 < 或 > 运算符,必须用 Cmp 方法判断大小关系 —— 这是 Go 里最容易写错的逻辑点之一。
为什么不能直接用 < 比较 *big.Int
因为 *big.Int 是指针类型,且 Go 不支持运算符重载。a < b 实际比较的是两个指针地址,和数值大小完全无关。一旦误用,逻辑永远出错,但编译器不报错,运行时也无 panic,极难排查。
-
a.Cmp(b) == -1表示a < b -
a.Cmp(b) == 0表示a == b -
a.Cmp(b) == 1表示a > b - 注意:
Cmp的接收者是左操作数,参数是右操作数,顺序不能反
Cmp 的常见误用场景和修复方式
典型错误出现在余额校验、Gas 限制检查、区块高度比对等地方。比如转账前写成 if balance < amount { ... },实际应为:
if balance.Cmp(amount) < 0 { // ✅ 正确:Cmp 返回 -1/0/1,可直接跟 0 比
return errors.New("insufficient balance")
}
- 不要写
balance.Cmp(amount) == -1—— 虽然语义对,但< 0更符合直觉且少打字符 - 避免在条件中重复调用
Cmp,如if a.Cmp(b) > 0 && a.Cmp(c) < 0,应缓存结果 - 如果要同时判断是否 ≥ 或 ≤,直接用
>= 0或<= 0,别拆成两个分支
从字符串或 hex 转换后比较前,必须确保解析成功
比如处理类似 "0xb6995e6b087fc99e0000" 这种十六进制字符串时,若直接用 strconv.ParseUint 会溢出,必须走 big.Int.SetString:
立即学习“go语言免费学习笔记(深入)”;
val := new(big.Int)
_, ok := val.SetString("0xb6995e6b087fc99e0000", 0) // 第二个参数 0 表示自动识别进制
if !ok {
return errors.New("invalid number format")
}
// 此时 val 才能安全参与 Cmp
-
SetString第二个参数填0可自动识别0x前缀,填16则强制十六进制(不认前缀) - 返回的
ok必须检查 —— 解析失败时val是零值,Cmp仍会返回结果,但逻辑已错 - 不要用
new(big.Int).SetString(...)然后直接比较,因为如果解析失败,这个临时对象仍是零值,掩盖了错误
最常被忽略的一点:Cmp 是唯一可靠的方式,且它不修改任何输入;但如果你在循环里高频调用,要注意 *big.Int 是可变对象,别把同一个实例反复传给多个 Cmp 却没意识到它可能被其他地方的 Add/Sub 改过值。


















