# 金额报表对不上账：用 Decimal 固定计算口径和舍入规则

## 文档明确的机制

Python 标准库 `decimal` 用于十进制定点和浮点运算，文档将其列为适合有严格相等不变量的会计场景的工具。原因是 `Decimal` 可以精确表示十进制数；文档示例指出，十进制浮点中 `0.1 + 0.1 + 0.1 - 0.3` 恰好等于零，而 binary float 会得到 `5.5511151231257827e-017`。这类差额虽接近零，却会影响可靠的相等比较，并可能累积。

`Decimal` 还保留有效位。文档示例中 `1.30 + 1.20` 得到 `2.50`，尾零会保留；乘法示例中 `1.30 * 1.20` 得到 `1.5600`。这使金额字段在进入计算时可以保留输入所表达的小数位意义。

计算环境由上下文控制。`getcontext()` 可以查看和修改精度、舍入规则以及 trap。文档显示默认精度为 28 位，默认舍入为 `ROUND_HALF_EVEN`；舍入选项还包括 `ROUND_CEILING`、`ROUND_DOWN`、`ROUND_FLOOR`、`ROUND_HALF_DOWN`、`ROUND_HALF_UP`、`ROUND_UP` 和 `ROUND_05UP`。

构造方式也很关键。文档说明 `Decimal` 可从整数、字符串、float 或元组构造，但从 float 构造会执行精确转换。示例中 `Decimal('3.14')` 是 `3.14`，而 `Decimal(3.14)` 是一长串二进制浮点对应的十进制值。因此，字符串金额应直接以字符串构造，而不是先变成 float。

报表落固定小数位时，文档明确提供 `quantize()`：它把数字舍入到固定指数，示例包括 `Decimal('7.325').quantize(Decimal('.01'), rounding=ROUND_DOWN)` 得到 `7.32`。格式化也可使用 `format()` 或 f-string，但文档示例同时显示，上下文中的舍入设置会影响格式化结果；因此展示前仍要明确采用什么位数和舍入规则。

异常处理由 signals 与 traps 组成。文档列出的信号包括 `InvalidOperation`、`DivisionByZero`、`Inexact`、`Rounded`、`Overflow`、`Underflow`、`FloatOperation` 等。每个信号都有 flag 和 trap enabler：遇到信号时先设置 flag；若 trap 开启，则抛出异常。flags 是粘滞的，监测计算前需要清除。

## 团队自行约定的接手建议

以下是团队实践建议，不是 `decimal` 文档声称的产品能力。

第一，金额字段从外部读入后先保留为字符串，在进入计算前统一用 `Decimal(字符串)` 构造。单价、数量、税额、优惠、汇总等字段分别登记来源、允许小数位和业务含义，避免把已经被 binary float 改写过的值再拿来对账。

第二，在报表任务开始前固定上下文。至少明确 `getcontext().prec` 使用什么精度、默认 rounding 使用哪一个规则；若业务要求四舍五入到分，应在相应的 `quantize()` 调用中明确使用 `ROUND_HALF_UP`，不要依赖默认的 `ROUND_HALF_EVEN`。

第三，把“中间计算”和“报表落表值”分开。中间金额先按约定上下文计算，到字段规定的节点再用 `quantize(Decimal('0.01'), rounding=...)` 固定到两位。哪些字段在明细行舍入、哪些字段按汇总后舍入，应由团队在口径表中写清楚；否则同一批数字可能因舍入位置不同而对不上。

第四，输出可复算信息。报表行可保留输入字符串、计算后 Decimal 值、quantize 后报表值，以及舍入或异常标记。需要监测信号时，在一组计算前清除 flags；若 trap 被打开，则让 `InvalidOperation`、`DivisionByZero` 等情况进入异常处理，而不是产出看似正常的金额。

第五，防止 Decimal 与 float 混算。文档说明二者通常不能直接进行算术运算，例如相加会抛出 `TypeError`；团队可约定所有金额只走 Decimal 路径，格式化放在最后一步。

## 边界与总结

Decimal 不会自动知道业务税率、会计政策或哪一层汇总应当舍入；这些规则必须由业务明确。文档还提示，`%` 在 Decimal 中结果符号跟随被除数，而不是像整数示例那样跟随除数；`//` 返回真商向零截断的整数部分。例如文档中整数 `-7 % 4` 为 `1`，而 `Decimal(-7) % Decimal(4)` 为 `-3`。

Decimal 与 float 混算会报错；格式化前仍必须确定展示位数和舍入方式。本文方案不能替代财务审核，只是把字符串输入、统一上下文、Decimal 计算、固定规则 quantize 和最终格式化排成可复算的流程，让报表差异更容易定位。

## 相关工具

[AI 工作流 ROI 诊断器](/demos/ai-workflow-roi-diagnostic/index.html) · 查看工具的输入要求、输出说明与适用范围。


来源：[金额报表对不上账：用 Decimal 固定计算口径和舍入规则](https://docs.python.org/3/library/decimal.html)
