当前位置:首页 > 文章列表 > Golang > Go教程 > Go Decimal 怎样从字符串构造精确小数

Go Decimal 怎样从字符串构造精确小数

来源:17golang原创 2026-10-29 11:59:18 0浏览 收藏

用 decimal.NewFromString("12.3405") 可以直接从十进制文本构造 shopspring/decimal 的 Decimal,并通过返回的 error 处理非法输入。需要保留原始十进制数值时,应让文本直接进入该函数,避免先经过 float64 再转换。

官方地址:https://github.com/shopspring/decimal

面对 CSV 导入或接口中的小数文本,我会先确认输入来源:既然源头已经提供字符串,就没有必要先换成二进制浮点。这是选型判断,下面用测量值累加说明;不把示例包装成一次真实事故或性能测试。

先按输入来源选择构造方式

输入本来是什么推荐入口取舍
十进制字符串NewFromString直接解析文本,保留数值;调用方处理错误
已有 float64NewFromFloat转换已有浮点值,不能还原之前丢失的原文信息
整数系数和明确单位New 或 NewFromInt整数来源清楚时简洁,但尺度必须约定

需要注意,NewFromFloat(0.1) 不是一定输出一大串尾数。当前文档将它描述为能转换回同一个 float64 的最短十进制表示。真正的问题是:如果上游已经把两个不同原始文本舍入成相同浮点值,下游再用 Decimal 就无法猜回区别。

例如整数文本 9007199254740993 超出了 float64 连续精确表示整数的范围。若业务希望保持该文本的精确数值,就应该直接解析字符串,而不是寄希望于后续转换修复。接口定义见 Decimal 构造方法文档。

用 NewFromString 接住原始文本

下面是一个完整的测量值累加示例。输入长度上限与去空白策略是应用主动选择的规则,库不替应用决定这些限制。项目需要在 go.mod 中声明并固定 github.com/shopspring/decimal 的版本;本文未执行代码,也没有附实际运行输出。

package main

import (
    "fmt"
    "log"
    "strings"

    "github.com/shopspring/decimal"
)

func parseReading(raw string) (decimal.Decimal, error) {
    // 先限制原始输入长度,避免无界文本进入后续解析。
    if len(raw) > 128 {
        return decimal.Zero, fmt.Errorf("测量文本过长")
    }
    // 本例明确接受首尾空白;严格协议可以改为直接拒绝。
    text := strings.TrimSpace(raw)
    if text == "" {
        return decimal.Zero, fmt.Errorf("测量值不能为空")
    }
    value, err := decimal.NewFromString(text)
    if err != nil {
        return decimal.Zero, fmt.Errorf("测量值格式错误: %w", err)
    }
    return value, nil
}

func sumReadings(raws []string) (decimal.Decimal, error) {
    total := decimal.Zero
    for i, raw := range raws {
        value, err := parseReading(raw)
        if err != nil {
            // 附上位置,调用方可定位需要修正的导入记录。
            return decimal.Zero, fmt.Errorf("第%d项: %w", i+1, err)
        }
        // Add 返回新值,必须接住返回结果。
        total = total.Add(value)
    }
    return total, nil
}

func main() {
    total, err := sumReadings([]string{"0.125", "2.375", "-0.5"})
    if err != nil {
        log.Print(err)
        return
    }
    // 显式固定三位用于展示;不把结果再转换成 float64。
    fmt.Println("合计:", total.StringFixed(3))
}

按十进制算术,上述数值合计为 2,固定三位的展示应为 2.000。这是例子的预期语义,不是运行回执。示例允许负值,因为测量修正量可能带符号;业务若只接受非负数,应在解析成功后单独限制。

结构图使用 原始文本、TrimSpace、NewFromString、Decimal、error、输入规则 六个正文节点:输入规则约束原始文本,TrimSpace 对应本例的清理选择,NewFromString 同时有成功值和错误结果两类出口。

十进制文本解析成功值与错误结果的静态结构图
结构图:文本清理、应用规则和解析结果各有职责;无向连线表示关联,不是操作流程或实测证据。

把解析失败和业务约束分别处理

我会把“解析器认识这个数字”和“业务允许这个数字”分成两件事。指数写法、符号、小数位数都可能合法表达一个数,但业务协议可能只允许固定格式。先确定协议,再选择保留或拒绝,不能因为库能解析就默认允许所有形式。

代码返回 decimal.Zero 时同时返回了 error,调用方必须先处理错误。忽略 error 会把不合法文本误当作真实的零。若需要区分空值与数值零,就在输入层额外保留有效性状态,不要仅靠 Decimal 的零值判断。

长度上限只是示例的第一层限制,并不能代替数值范围和指数范围限制。接收不可信输入时,还应按协议限制指数形式、有效数字和运算规模;特别大的指数可能在展开成普通十进制文本时带来额外资源消耗。不要随意给 parser 接受的全部数值都调用超大位数格式化。

解析函数如何分解十进制文本,可参考 项目 decimal.go;外部输入的业务规则应由应用明确维护。

不要让格式化重新丢失精度

构造精确数值后,我更愿意继续在 Decimal 之间计算,边界输出用十进制字符串。若中途又转换为 float64,可能重新引入二进制浮点的表示限制。

图中 Decimal值、Add、累加结果 归入数值计算;StringFixed、展示文本、原始文本 归入文本表达。Add 使用 Decimal 值并产生累加结果,StringFixed 与结果、展示文本相关;原始文本作为单独保留的信息,与数值对象有来源关系。

Decimal数值计算与文本显示职责的静态关系图
结构图:精确数值、固定显示和原始文本是不同层面的数据,不要用展示字符串反推输入格式。

StringFixed(3) 控制固定三位小数的展示,位数不足会补齐,过多会按该方法的舍入规则输出。它不会把原数值永久限制成三位,也不代表原文恰好有三位。若原文是 +001.2300,Decimal 负责数值,原有加号、前导零等排版信息应另存。

解析的精确性也不意味着所有后续运算都无限精确。例如不能整除的除法仍需精度与舍入策略。本文聚焦构造、加法和显示,没有给所有运算承诺统一的精度行为。

哪些场景不必引入 Decimal

如果数据本来就是计数器,只需 int64 就能表达,Decimal 会增加不必要的复杂度。如果是容许近似误差的科学计算或传感器算法,float64 可能更符合库生态和计算需求。若协议固定以最小单位传整数,整数系数加明确尺度也能很好地工作。

我的选择是有条件的:原始输入为十进制文本,而且数值必须按十进制语义保留时,优先直接 NewFromString;已经只有 float64 时,接受它代表的现有信息,必要时回溯上游输入协议,而不是把“转成 Decimal”当成信息恢复工具。

是不是必须先 ParseFloat,再 NewFromFloat?

不需要。输入已经是数字字符串时,这样绕行反而引入额外的表示转换。直接 NewFromString,并检查 error。

字符串里带空格能不能直接解析?

本文明确在应用层调用 TrimSpace。这样是否符合协议由调用方决定,不能把本例的去空白行为误当作库对所有空白形式的保证。

需要保留输入中的尾随零怎么办?

数值尺度、默认字符串展示与原始字符逐字保留是不同要求。需要固定显示位数可选 StringFixed;需要保留用户原文则另外存储原始文本,不要依赖默认 String 格式完成原文存档。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Go Flock 怎样区分共享锁和排他锁的适用范围Go Flock 怎样区分共享锁和排他锁的适用范围
上一篇
Go Flock 怎样区分共享锁和排他锁的适用范围
Go Decimal 怎样用 RoundBank 实现银行家舍入
下一篇
Go Decimal 怎样用 RoundBank 实现银行家舍入
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    773次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    770次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    854次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    821次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    651次使用