Go big.Rat 怎样保存精确小数计算
来源:17golang原创
时间:2026-10-14 19:32:03 105浏览 收藏
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 保持精确计算,让存档和显示承担不同职责。
-
860 收藏
-
843 收藏
-
826 收藏
-
809 收藏
-
792 收藏
-
339 收藏
-
265 收藏
-
497 收藏
-
307 收藏
-
135 收藏
-
427 收藏
-
274 收藏
-
439 收藏
-
353 收藏
-
326 收藏
-
486 收藏
-
323 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习