Go big.Rat 怎样保存精确小数计算
Go big.Rat 保存精确小数计算的做法是:从十进制字符串调用 SetString,运算保持在 Rat 中,存档使用 RatString,读回再用 SetString。FloatString(2) 适合显示两位小数,却可能舍入,不能普遍代替精确存档。
输入小数先保留原始字符串
假设一个换算功能收到字符串“10.25”和“0.15”,需要保留乘法结果,而页面只显示两位。这里的技术目标是让保存值保持 1.5375,不提前变成 1.54;示例不代表真实结算规则。
字符串是数值来源的一部分。若先 ParseFloat,再 SetFloat64,Rat 保存的是那个浮点数本身的精确值,而不是自动恢复原始十进制。例如通常的 float64 0.1 不是数学意义上的 1/10,换成有理数不会抹掉前面的表示误差。
官方接口文档:https://pkg.go.dev/math/big#Rat。下面围绕字符串与 Rat 的边界组织实现,不展开数据库、货币单位或结算制度。
把解析失败挡在运算之前
SetString 接受的格式比普通小数输入宽,包含分数和带指数的数字。库能解析不代表业务应该接受。演示中的 parseDecimal 先限制为带可选正负号的普通十进制,最多64字节,不接受空白、分数、指数和千位分隔符;具体项目还应按需求规定数值范围和小数位数。
必须检查返回的 ok。解析失败时返回 nil,接收者的内容不能作为有效值使用。不要用旧对象反复尝试解析后,失败仍继续计算,也不要默默把非法输入替换成零。分数存档与普通小数输入是两种格式,恢复入口不能错误地套用这个仅允许十进制的正则。

用 Rat 保存精确值而不是显示位数
Rat 是有理数,不是自动附带“保留两位”的十进制字段。它表达分子与分母的比值:有限小数能精确表达,1/3 这样的循环小数也能精确保存在有理数模型里。它不保留原输入中的尾随零;“1.20”和“1.2”具有相同数值,若需要保存原排版,应另存原始字符串或业务尺度。
math/big 运算写入接收者。使用 new(big.Rat).Mul(amount, factor) 得到独立结果,输入保持不变;若把 amount 自己当接收者,它的值就会被覆盖。精确计算不是无限资源,输入长度、运算规模和显示位数都需要预算。
保存分数字符串并恢复计算
下面完整 main.go 展示输入解析、乘法、保存和恢复;不涉及文件或数据库写入,saved 就是待交给存储层的字符串。
package main
import (
"fmt"
"log"
"math/big"
"regexp"
)
// 示例仅收普通十进制,拒绝分数、指数、空白和额外字符。
var decimal = regexp.MustCompile(`^[+-]?[0-9]+(\.[0-9]+)?$`)
func parseDecimal(s string) (*big.Rat, error) {
// 长度64是演示输入预算,不是通用金额业务规则。
if len(s) == 0 || len(s) > 64 || !decimal.MatchString(s) {
return nil, fmt.Errorf("小数字符串格式或长度不符合要求")
}
r, ok := new(big.Rat).SetString(s)
if !ok {
// 失败接收者的内容不可继续使用。
return nil, fmt.Errorf("小数字符串无法解析")
}
return r, nil
}
func main() {
amount, err := parseDecimal("10.25")
if err != nil { log.Fatal(err) }
factor, err := parseDecimal("0.15")
if err != nil { log.Fatal(err) }
// 独立接收者保留输入对象;示例只是十进制乘法。
result := new(big.Rat).Mul(amount, factor)
saved := result.RatString()
restored, ok := new(big.Rat).SetString(saved)
if !ok {
log.Fatal("保存字符串恢复失败")
}
// 字符串显示可舍入,但保存和恢复仍保留精确数值。
fmt.Println("保存值:", saved)
fmt.Println("显示值:", result.FloatString(2))
fmt.Println("恢复相等:", restored.Cmp(result) == 0)
// 循环小数也能以有理数形式无损保存。
third := big.NewRat(1, 3)
fmt.Println("三分之一保存:", third.RatString())
fmt.Println("三分之一显示:", third.FloatString(2))
}按数学计算推导,保存值为 123/80,显示值为 1.54,恢复比较为 true;三分之一保存为 1/3,显示为 0.33。这些是示例预期,不是本机运行记录。真正存档时字段类型应能保留整个字符串,不能让接口再把它转成 float64。
RatString 在分母为1时可以只输出整数;String 则总是 a/b 形式。选定一种作为接口约定,保存时注明是有理数字符串,避免调用方把“123/80”当作十进制数解析。读回外部存档也要限制长度并检查 SetString 成功,不能因来源叫“存档”而完全信任。

别把显示舍入当作精确结果
| 用途 | 采用方式 | 保留什么 |
|---|---|---|
| 继续精确计算 | Rat 运算 | 有理数值 |
| 无损存档 | RatString 后 SetString | 完整数值,不保留原排版 |
| 固定小数显示 | FloatString(位数) | 舍入后的显示文本 |
| 原输入格式 | 另存输入字符串 | 尾随零等文字信息 |
FloatString 按小数位数生成文本,末位按最近值舍入,恰好一半时远离零,不是默认银行家舍入。给 1/3 输出两位只能得到近似显示;把“0.33”再解析回 Rat,得到的是 33/100,不再是 1/3。Rat 本身不会因为调用 FloatString 而变成舍入后的值。
因此原场景的取舍很明确:存档用精确字符串,显示独立处理。若业务明确要求某一环节量化到固定尺度,需要另外定义舍入时点和规则,不能把显示函数直接当成所有场景的处理规范。
结束计算时保持对象所有权清楚
Rat 对象不支持结构体浅拷贝。需要独立副本时用 new(big.Rat).Set(original),不要写 copied := *original 再分别修改。多个 goroutine 共享可变 Rat 时仍需自己的同步和所有权规则;精确算术不等于并发修改安全。
这些对象没有文件句柄可关闭。局部计算结束后,不再持有临时对象引用即可让垃圾回收管理内存;不要在 long-lived 容器里无界保存每次计算的临时对象。这里的生命周期管理是输入、结果、存档文本各自负责,不是增加一个清理 API。
能用 FloatString 保存全部精度吗?
对任意 Rat 不能保证。循环小数没有有限位数的无损十进制表示,优先保存分数字符串。
为什么 1.200 的三个小数位不见了?
Rat 保存数值,不保存原字符串尺度。需要原样显示时另存输入文本。
解析失败后可以继续使用接收者吗?
不能依赖其值,必须停止这一条输入的计算并返回错误。除法还应另行检查除数为零,本文没有实现通用除法接口。
字符串转换实现可对照 https://go.dev/src/math/big/ratconv.go。完整方案的关键是让字符串保持十进制来源,让 Rat 保持精确计算,让存档和显示承担不同职责。
Go 怎样拆分浮点数的尾数和指数
- 上一篇
- Go 怎样拆分浮点数的尾数和指数
- 下一篇
- Go filepath.Rel 怎样计算两个路径的相对关系
-
- Golang · Go教程 | 17分钟前 |
- Go 模糊测试失败后怎样保留回归语料
- 339浏览 收藏
-
- Golang · Go教程 | 19分钟前 | 测试 · go · testing.F.Add testdata/fuzz Go模糊测试 Go种子语料
- Go 模糊测试怎样组织种子语料
- 265浏览 收藏
-
- Golang · Go教程 | 20分钟前 |
- Go t.Run 怎样设计稳定的子测试名称
- 497浏览 收藏
-
- Golang · Go教程 | 24分钟前 |
- Go t.TempDir 怎样管理测试临时文件
- 307浏览 收藏
-
- Golang · Go教程 | 27分钟前 |
- Go 测试怎样为 t.Parallel 隔离可变状态
- 135浏览 收藏
-
- Golang · Go教程 | 28分钟前 | go · 运行时 · runtime.KeepAlive finalizer Go系统调用
- Go runtime.KeepAlive 怎样保护外部资源参数
- 427浏览 收藏
-
- Golang · Go教程 | 29分钟前 |
- Go finalizer 怎样避免对象环导致资源迟迟不释放
- 274浏览 收藏
-
- Golang · Go教程 | 31分钟前 |
- Go 怎样理解 GOMAXPROCS 默认值调整
- 439浏览 收藏
-
- Golang · Go教程 | 33分钟前 |
- Go RWMutex TryRLock 适合什么探测场景
- 353浏览 收藏
-
- Golang · Go教程 | 36分钟前 |
- Go WaitGroup.Go 怎样组织任务计数
- 326浏览 收藏
-
- Golang · Go教程 | 38分钟前 |
- Go sync.Pool 怎样理解对象被回收的时机
- 486浏览 收藏
-
- Golang · Go教程 | 39分钟前 |
- Go slices.Values 怎样遍历切片而不暴露索引
- 323浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- PubMedQA
- 深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
- 473次使用
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 531次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 551次使用
-
- HELM
- 深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
- 503次使用
-
- MMBench
- MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
- 318次使用
-
- Java 性能优化上线清单:从定位、改造到灰度发布
- 2026-06-11 860浏览
-
- Spring Boot 压测验证:Gatling、JMeter 与性能回归门禁
- 2026-06-11 843浏览
-
- Java NMT 非堆内存排查:Direct Buffer、线程栈与 Metaspace 分析
- 2026-06-11 826浏览
-
- Spring Boot 容器内存优化:JVM 堆、非堆与 MaxRAMPercentage
- 2026-06-11 809浏览
-
- Tomcat 连接与线程参数调优:maxThreads、acceptCount 与 KeepAlive
- 2026-06-11 792浏览
