登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  Golang >  Go教程

Go math/big 怎么读取大整数

来源:17golang原创

时间:2026-09-13 10:45:07 474浏览 收藏

在 Go 里,超过内置整数范围的值不要先放进 int64 再转换,直接交给 math/big.Int 更稳妥。最常见的读取入口是:十进制或其他进制文本用 SetString,流式输入用 fmt.Sscan,JSON 字段可直接让 json.Unmarshal 调用 UnmarshalJSON,大端字节则用 SetBytes

如果输入来自字符串,优先使用 SetString(s, 0) 并同时检查返回值;如果来自 JSON 或二进制协议,分别使用对应的反序列化方法。读取完成后用 String()Text(base) 输出,转回 int64 前先调用 IsInt64()

要点速览
  • SetString 要检查 ok,成功要求整个字符串都合法。
  • base=0 会按 0b0o0x 等前缀识别进制。
  • BytesSetBytes 表示无符号大端绝对值,不携带负号。

先按输入形态选择 math/big 读取入口

big.Int 是有符号多精度整数,零值可以直接使用。它的 API 采用指针接收者,读取和运算都要让目标对象接收结果。不要把 big.Int 当作普通数值类型直接做强制转换。

输入入口需要注意
文本SetString(s, base)返回值为 (*Int, bool)
fmt 流fmt.Sscan / fmt.Fscan底层调用 Int.Scan
JSONjson.Unmarshal目标必须是 *big.Int
大端字节SetBytes按无符号绝对值解释

用 SetString 读取大整数文本

配置文件、CSV 字段和表单值通常是字符串。固定十进制可以传 10;想同时接受十进制与 0x0o0b 前缀,则传 0。成功条件是整个字符串都符合指定进制,不能只解析前半段。

package main

import (
    "fmt"
    "math/big"
)

func readBigDecimal(s string) (*big.Int, error) {
    // base=10 只接受十进制,避免输入前缀改变协议含义。
    n, ok := new(big.Int).SetString(s, 10)
    if !ok {
        return nil, fmt.Errorf("非法大整数: %q", s)
    }
    return n, nil
}

func main() {
    n, err := readBigDecimal("18446744073709551617")
    if err != nil {
        panic(err)
    }
    fmt.Println(n.String()) // String 默认输出十进制文本。
}
Go math/big 大整数读取入口与 big.Int 的静态关系示意图
图1:Go math/big 大整数读取入口的原创结构示意图,文本、JSON 和大端字节分别连接到 big.Int。

这里不能只判断 n != nilok 才表示解析成功。失败时接收者的值没有可依赖的定义,因此应直接丢弃并返回错误。

流式输入和 JSON 让 big.Int 自己完成读取

big.Int 实现了 fmt.Scanner,所以可以把它交给 fmt.Sscanfmt.Fscan。这种方式适合空白分隔的文本流。JSON 场景则应传指针给 json.Unmarshal;不要先用 float64 接收,否则大整数可能在进入 big.Int 前已经失真。

var fromText big.Int
if _, err := fmt.Sscan("18446744073709551617", &fromText); err != nil {
    // 扫描错误要向上返回,不能把零值当成读取结果。
    return err
}

var fromJSON big.Int
if err := json.Unmarshal([]byte(`18446744073709551617`), &fromJSON); err != nil {
    // 直接解码到 big.Int,保留 JSON 数字的完整整数文本。
    return err
}

如果 JSON 字段是字符串而不是数字,先取字符串再用 SetString,并明确约定是否允许前缀。这样能把协议格式和进制规则放在同一处处理。

读取后怎么输出,避免转换时悄悄溢出

String() 等价于 Text(10),用于普通十进制输出;需要十六进制、二进制或更高进制时使用 Text(base)Text 的进制范围是 2 到 62,输出不会自动带 0x 前缀。协议要求固定字节时,Bytes() 返回大端字节,但它只表达绝对值。

func exportBig(n *big.Int) (string, []byte, error) {
    // 只有确认能放进 int64,才允许做窄化转换。
    if !n.IsInt64() {
        return "", nil, fmt.Errorf("数值超出 int64 范围")
    }
    decimal := n.String()
    hexText := n.Text(16)
    bytes := n.Bytes() // 负数的符号不在 bytes 中,协议需另存符号。
    return decimal + " / 0x" + hexText, bytes, nil
}
Go big.Int 输出到 String Text Bytes 和 int64 的静态边界示意图
图2:big.Int 输出与窄化转换的原创结构示意图,String、Text、Bytes 和 IsInt64 分别对应不同边界。

四个边界问题最容易让读取结果出错

  • 空串或尾部字符:SetString("123x", 10) 失败;不要把它当作成功解析了 123。
  • 进制前缀:base=10 不负责识别 0x,要识别前缀请传 0,或者先在协议层清洗。
  • 负数与字节:Bytes()SetBytes() 是无符号大端绝对值通道,负号需要单独设计。
  • 窄化转换:Int64() 在超范围时结果未定义,先用 IsInt64();同理,转无符号值前检查 IsUint64()

常见问题

SetString 的 base 应该写 0 还是 10?

输入协议固定为十进制就写 10;需要让标准前缀选择进制时写 0。两者都要检查返回的 ok

big.Int 能直接用 fmt.Println 输出吗?

可以。*big.Int 提供默认的十进制字符串表示;需要其他进制时显式调用 Text

为什么 SetBytes 读不出负数?

该方法把字节解释为大端无符号整数,返回的是绝对值。带符号协议应把符号位或符号字段单独解析。

读取成功后能直接调用 Int64 吗?

只有范围确认后才能调用。用 IsInt64 判断,超范围就继续保留 *big.Int 或返回错误。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>