用 Python Decimal 汇总 CSV 金额:避免浮点误差的入门示例

举报
yd_217337598 发表于 2026/10/21 23:53:28 2026/10/21
【摘要】 从 CSV 金额汇总出发,介绍 Decimal 的字符串构造、缺失值与 NaN 检查,并说明金额舍入与上下文精度应如何按业务规则确定。

金额报表里,最难排查的问题往往不是程序报错,而是结果只差几分钱。本文用 Python 标准库读取 CSV,演示怎样保留金额精度,并把坏数据明确暴露出来。示例只在本地读文件,不依赖云资源或付费服务。

一、先定义输入约定

假设 orders.csv 使用 UTF-8 编码,第一行是表头,至少包含 amount 列。每一行金额采用十进制字符串,例如 19.90、0.10 或 -5.00;负数代表退款。空值不默认为零,因为缺失金额与真实的零是两种不同业务含义。示例也不接受千分位逗号或货币符号,避免把格式问题悄悄掩盖。

二、为什么不用 float

二进制浮点数不能精确表示所有十进制小数。例如,0.1 与 0.2 的浮点相加结果,直接显示时可能带有额外尾数。报表使用 Decimal 可以按十进制处理数值。关键是直接传入原始字符串:Decimal('0.10');不要先转成 float 再构造 Decimal,否则此前产生的近似值也会被带入。

三、可以直接运行的代码

from csv import DictReader
from decimal import Decimal, InvalidOperation

def total_amount(path):
    total = Decimal('0')
    with open(path, encoding='utf-8-sig', newline='') as f:
        reader = DictReader(f)
        if not reader.fieldnames or 'amount' not in reader.fieldnames:
            raise ValueError('缺少 amount 表头')
        for record_no, row in enumerate(reader, start=1):
            raw = (row.get('amount') or '').strip()
            try:
                amount = Decimal(raw)
            except InvalidOperation as exc:
                raise ValueError(f'第 {record_no} 条记录金额非法:{raw!r}') from exc
            if not amount.is_finite():
                raise ValueError(f'第 {record_no} 条记录金额必须为有限数')
            total += amount
    return total

print(total_amount('orders.csv'))

utf-8-sig 可以兼容常见的 UTF-8 BOM 文件;newline='' 让 csv 模块处理换行。这里报告的是数据记录序号,不是文件物理行号,因为被引号包围的 CSV 字段可能跨行。is_finite() 用来拒绝 NaN 和 Infinity,避免这类特殊值污染汇总。

四、用最小样本核对结果

把下面三笔写入 amount 列:0.10、0.20、-0.05。预期总额是 0.25。再分别测试空金额、abc 和 NaN,应得到明确错误,而不是一个看似正常的总数。遇到编码错误时,应先确认文件编码,再选择正确的 encoding;不要用忽略错误的方式跳过字符。

五、把精度规则放进业务约定

Decimal 仍受当前上下文精度限制,并不等于无限精度。一般短金额示例可用默认上下文;超长数值和大规模累计应依据最大位数评估精度。是否按两位小数四舍五入、采用哪种舍入方式、逐笔舍入还是汇总后舍入,都需要由业务规则决定。不要为了让数字“好看”就随意 round。

这个示例的重点是三个边界:金额从字符串进入、异常数据及时停止、舍入策略显式约定。先把这三件事做好,再扩展分组汇总、日志和报表导出,会更容易核对结果。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。