TT Lab
开始
学习 学习路径 课程

银行现场的语言

合计差三元,可每一行都没错

在 TT Lab 中继续学习

目标

把各货币不同的最小单位做成表,用整数最小单位重新加载手续费账本,把舍入模式钉成策略,并把连多出的那 1 也能对上的分配,以及折算顺序造成的差别,作为产出物留下来。

为什么重要

如果用实数处理金额,每一行就会差 1,而这个程度,单元测试抓不到。即使收到合计对不上的咨询,也指不出是哪一行错了,于是就形成了“重新跑一遍就对了”的运维流程。 这里要定的有三件事。存储格式(整数最小单位)、截取到哪一位和舍入模式(策略)、余数的分配规则(最大余数法)。这三件事技术都不会替你定,需要由人来定,并写进文档。 评分器不会拿你写下的数字互相比较。它会从第 1 步做出的原始数据重新计算基准值来对照。所以原样遵守规则很重要。

步骤

  1. 创建并运行 /root/money/gen_money.py,生成 /root/money/currencies.json、/root/money/fees.csv(600 行)和 /root/money/splits.csv(30 件)。
  2. 把加成率 0.125 分别用实数路径和整数路径计算,把出现偏差的行数和差额写入 /root/money/float_gap.json。
  3. 在 /root/money/money.db 中以 fee(整数最小单位)和 fee_real(REAL)两张表加载,并把各货币的两个合计写入 /root/money/store.json。
  4. 把解析和显示收拢到 /root/money/money_fmt.py,对说明中的 12 个输入作出判定,生成 /root/money/format_check.json。
  5. 把同样的乘法分别用 ROUND_HALF_UP 和 ROUND_HALF_EVEN 截取,把出现分歧的行数和两个合计写入 /root/money/rounding.json。
  6. 用最大余数法分配 splits.csv 的 30 件,写入 /root/money/alloc.json。合计必须与总额恰好相等。
  7. 用说明中的汇率表,把 5 种货币折算成韩元,把按行和按总额两种值写入 /root/money/fx.json。
  8. 把前面各步骤的结果汇总到一个 /root/money/report.json 中,并一起写上 fees.csv 的 sha256。

参考

生成货币表和手续费账本

创建并运行 /root/money/gen_money.py,生成 /root/money/currencies.json、/root/money/fees.csv(600 行)和 /root/money/splits.csv(30 件)。

货币表的小数位数,直接使用阅读材料中写的 List One 的值。小数位数为 N.A. 的货币不是数字,所以是 null。amount_text 是把最小单位整数做成的文字,所以 0 位货币没有小数点。

用实数计算的加成金额有多少行出现偏差

把加成率 0.125 按说明中的两条路径计算,并把 rate、rows、mismatch_rows、gap_minor_total 和 by_ccy 写入 /root/money/float_gap.json。

两条路径的差别来自两处。0.125 在二进制中是精确的,但 12.345 这样的金额就不是,而且 Python 内置的 round() 会把 0.5 靠向偶数一侧。by_ccy 是为每种货币放入 mismatch_rows 和 gap_minor 两个值的对象。

用整数最小单位重新加载

在 /root/money/money.db 中创建 fee(row_id, ccy, minor, exponent)和 fee_real(row_id, ccy, amount REAL)两张表,各放入 600 行,并把各货币的 sum_minor、real_round_minor 和 equal 写入 /root/money/store.json。

SQLite 的数据类型附着在值上,而不是列上。即使声明为 INTEGER,放入实数也会以 REAL 存入,请用 typeof() 确认一下。real_round_minor 是把 REAL 的合计乘以该货币的小数位数对应的倍数后舍入得到的整数。equal 也可能全部是 true——在这个规模下,REAL 之和碰巧会舍入成同一个值,所以这类缺陷才会长期存活。请照实写。

把解析和显示收拢到一道门

在 /root/money/money_fmt.py 中做出 parse 和 fmt,对说明中的 12 个输入作出判定,并在 /root/money/format_check.json 中写成 ok 和 rejected。

ok 项中放入 in、ccy、minor 和 round_trip。round_trip 是把最小单位重新做成文字后的值,必须与原来的写法相同。拒绝的原因只有四种,完全不认识货币,与不知道小数位数,是不同的原因。

把舍入模式钉成策略

把同样的乘法分别用 ROUND_HALF_UP 和 ROUND_HALF_EVEN 截取,并把 policy、rate、diff_rows、half_up_total_minor 和 half_even_total_minor 写入 /root/money/rounding.json。本实验的策略是 ROUND_HALF_UP。

乘法只做一次,quantize 做两次就行。0.125 在二进制中是精确的值,所以经常会出现恰好一半的位,两种模式就是在那里产生分歧的。如果两个合计相同,说明其中一边实际上没有被应用。

连多出的那 1 也能对上的分配

用最大余数法把 splits.csv 的 30 件分配到 3 处,并把 cases、sum_ok 和 remainder_total 写入 /root/money/alloc.json。

商按向下取整给出,只把剩下的部分每处多给 1。由谁得到,按余数(总额乘以权重再除以权重之和所得的余数)从大到小的顺序,相同则编号靠前的先拿。remainder_to 按升序写出多拿了 1 的位置从 0 开始数的编号。

折算顺序会改变结果

用说明中的汇率表把 5 种货币折算成韩元,并把 rate_scale、round_mode 和 by_ccy(rate, rows, row_then_sum_krw, sum_then_row_krw, gap_krw)写入 /root/money/fx.json。

逐行折算、按韩元单位截取再相加的值,与先全部相加之后只折算一次的值,是不同的。两个都要计算,并把差别原样写出来。韩元的小数位数是 0,所以截取的位是 1 韩元。

把六个步骤汇总成一份报告

在 /root/money/report.json 中写入 fees_sha256、rows、currencies、exact_total_minor、float_gap_rows、float_gap_minor、rounding_policy、half_even_diff_rows、allocation_cases、allocation_remainder_total、fx_gap_krw 和 verdict。verdict 是 integer-minor-units。

读取前面步骤的产出物来汇总,但哈希要现在从 fees.csv 重新计算。如果报告指不出它依据的是哪份数据,日后就无法解释这些数字。exact_total_minor 是各货币的最小单位合计。