金额差一分钱怎么查:Decimal 的输入与舍入时机
金额对不上时,先记录原始十进制字符串、舍入规则和舍入发生的位置。使用 Decimal 后,逐行舍入再求和与求和后舍入仍可能产生不同结果,接口和账单必须选定同一套规则。
1. 三笔小数就能复现差额
保存为 decimal_lab.py,运行 python decimal_lab.py。这里演示非负金额,最多十二位整数和四位小数,舍入到两位小数采用 ROUND_HALF_UP。这是本例的业务约定。
import re
from decimal import Decimal, ROUND_HALF_UP, localcontext
CENT = Decimal("0.01")
def parse_amount(text):
if not isinstance(text, str) or not re.fullmatch(r"(0|[1-9][0-9]{0,11})(\.[0-9]{1,4})?", text):
raise ValueError("invalid amount")
return Decimal(text)
def settle(amount):
return amount.quantize(CENT, rounding=ROUND_HALF_UP)
with localcontext() as ctx:
ctx.prec = 32
exact = parse_amount("2.675")
assert settle(exact) == Decimal("2.68")
assert settle(Decimal(2.675)) == Decimal("2.67")
rows = [parse_amount("0.005")] * 3
per_line = sum(map(settle, rows), Decimal("0"))
on_total = settle(sum(rows, Decimal("0")))
assert (per_line, on_total) == (Decimal("0.03"), Decimal("0.02"))
print("from string:", settle(exact))
print("from float:", settle(Decimal(2.675)))
print("per line:", per_line, "on total:", on_total)
for invalid in ["NaN", "Infinity", "1e9", "-1", "1.23456"]:
try:
parse_amount(invalid)
except ValueError:
pass
else:
raise AssertionError(invalid)
print("invalid inputs: rejected")
2. 把差额定位到具体步骤
| 实测结果 | 发生了什么 | 修复位置 |
|---|---|---|
| 字符串输入得到 2.68,float 输入得到 2.67 | float 在进入 Decimal 前已有二进制表示误差 | 从 JSON 字符串或原始十进制文本解析 |
| 逐行结算 0.03,整单结算 0.02 | 三次舍入与一次舍入产生不同累计值 | 明确行项目和总额的结算顺序 |
| NaN、指数形式或过多小数被拒绝 | 输入超出本例契约 | API 返回字段校验错误 |
从客户端到数据库都要保留金额的表示约定。若客户端先算成浮点数再转成字符串,服务端收到的误差已经存在。整数分适合已经结算到分的金额,折扣比例和中间计算仍需要独立精度。
3. 分摊折扣时验收总和
例如将 1.00 平均分给三行,先分配 0.33、0.33、0.33,还剩 0.01。按固定规则把余数给某一行,例如按行 ID 排序后分配。验收时同时检查每行精度、各行之和、负数或退款的规则。将分摊结果存下来,后续退款引用原结果。
示例上下文精度 32 足够覆盖这些小数据。批量汇总和乘除运算需要根据金额上限、数量和中间结果重新确定精度。新增舍入规则时,用半分边界和正负数各做一组回归用例。
小结
保留十进制输入,显式指定舍入规则,并在固定的业务步骤结算。对账时同时保留行金额和总金额,才能定位差额发生在哪一次计算。
数据来源与检索时间
检索时间 2026-11-12。
| 来源 | URL | 对应内容 |
|---|---|---|
| Python 3.11 官方文档 | https://docs.python.org/3.11/library/decimal.html | Decimal 构造、上下文精度与 quantize |
核验说明
- 主示例在本地 Python 3.11.9 下执行,检查正文中的断言与输出。
- 实验范围是文中构造的输入和本地运行条件,部署时需按实际数据规模与运行环境核验。