FAILURE MAP
← Case archive

FA-62236 / Currency rounding and FX conversion / Open access

Amounts dated on the cutover day are redenominated a second time · case 01

Payments booked on the cutover date are divided by the factor although they are already in the new currency.

Verified by executionVariant 1 · 8 checks per implementationDownload source bundle ↓JSON ↗

ROOT CAUSE

The native test uses date > cutover.

VERIFIED REPAIR

Treat amounts dated on or after the cutover as native.

Unsuccessful approach: Comparing only year and month treats earlier days of the cutover month as native.

Case contract

solve(amount_minor, date, cutover, factor, old_exp, new_exp): amounts dated on or after the cutover (ISO date strings) are already in the new currency and returned unchanged as [amount_minor, 'native']. Earlier amounts are old-currency minor units: new = amount_minor / factor * 10**(new_exp - old_exp), rounded half away from zero, returned as [new, 'converted']. factor is old units per new unit.

Why this case matters

Currency amounts must be rounded at the right stage and in the right unit, or ledgers, quotes and settlements drift by minor units.

1 / The failure

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json
from decimal import Decimal, ROUND_HALF_EVEN, ROUND_HALF_UP, ROUND_FLOOR, ROUND_CEILING, ROUND_DOWN, ROUND_UP, ROUND_HALF_DOWN
N = 1
observations = []
def solve(amount_minor, date, cutover, factor, old_exp, new_exp):
    if date > cutover: return [amount_minor, 'native']
    v = Decimal(amount_minor) / factor * Decimal(10) ** (new_exp - old_exp)
    return [int(v.quantize(Decimal(1), rounding=ROUND_HALF_UP)), 'converted']
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
cases = [[('regression cutover-boundary 1', (500, '2026-07-01', '2026-07-01', 10000, 2, 2), [500, 'native']),
  ('regression cutover-boundary 2', (7086273, '2026-07-15', '2026-07-15', 1000, 0, 3), [7086273, 'native']),
  ('partial repair guard 1', (99999, '2026-07-15', '2026-07-20', 100, 2, 2), [1000, 'converted']),
  ('partial repair guard 2', (13591002, '2026-07-05', '2026-07-15', 1000, 2, 3), [135910, 'converted']),
  ('control: before cutover', (1234567, '2026-06-30', '2026-07-01', 10000, 2, 2), [123, 'converted']),
  ('control: negative tie', (-15000, '2026-01-01', '2026-07-01', 10000, 2, 2), [-2, 'converted']),
  ('control: exponent gain', (1234567, '2025-12-31', '2026-01-01', 1000, 0, 2), [123457, 'converted']),
  ('control: after', (77, '2027-01-01', '2026-07-01', 10000, 2, 2), [77, 'native'])],
 [('regression cutover-boundary 1', (23477950, '2026-07-01', '2026-07-01', 10000, 2, 3), [23477950, 'native']),
  ('regression cutover-boundary 2', (7192253, '2026-07-01', '2026-07-01', 10000, 2, 3), [7192253, 'native']),
  ('partial repair guard 1', (10748129, '2026-07-06', '2026-07-20', 10000, 0, 2), [107481, 'converted']),
  ('partial repair guard 2', (13003559, '2026-07-14', '2026-07-20', 10000, 2, 3), [13004, 'converted']),
  ('control: exponent gain', (1234567, '2025-12-31', '2026-01-01', 1000, 0, 2), [123457, 'converted']),
  ('control: same month earlier', (99999, '2026-07-15', '2026-07-20', 100, 2, 2), [1000, 'converted']),
  ('control: after', (77, '2027-01-01', '2026-07-01', 10000, 2, 2), [77, 'native']),
  ('control: small old amount', (4999, '2026-06-01', '2026-07-01', 10000, 2, 2), [0, 'converted'])],
 [('regression cutover-boundary 1', (12703023, '2026-07-15', '2026-07-15', 1000, 2, 2), [12703023, 'native']),
  ('regression cutover-boundary 2', (500, '2026-07-01', '2026-07-01', 10000, 2, 2), [500, 'native']),
  ('partial repair guard 1', (20559948, '2026-07-01', '2026-07-20', 10000, 0, 3), [2055995, 'converted']),
  ('partial repair guard 2', (9508148, '2026-07-07', '2026-07-20', 1000, 0, 3), [9508148, 'converted']),
  ('control: small old amount', (4999, '2026-06-01', '2026-07-01', 10000, 2, 2), [0, 'converted']),
  ('control: before cutover', (1234567, '2026-06-30', '2026-07-01', 10000, 2, 2), [123, 'converted']),
  ('control: negative tie', (-15000, '2026-01-01', '2026-07-01', 10000, 2, 2), [-2, 'converted']),
  ('control: exponent gain', (1234567, '2025-12-31', '2026-01-01', 1000, 0, 2), [123457, 'converted'])],
 [('regression cutover-boundary 1', (7086273, '2026-07-15', '2026-07-15', 1000, 0, 3), [7086273, 'native']),
  ('regression cutover-boundary 2', (23477950, '2026-07-01', '2026-07-01', 10000, 2, 3), [23477950, 'native']),
  ('partial repair guard 1', (1287988, '2026-07-12', '2026-07-15', 100, 0, 3), [12879880, 'converted']),
  ('partial repair guard 2', (20017420, '2026-07-14', '2026-07-15', 100, 0, 0), [200174, 'converted']),
  ('control: cutover day', (500, '2026-07-01', '2026-07-01', 10000, 2, 2), [500, 'native']),
  ('control: exponent gain', (1234567, '2025-12-31', '2026-01-01', 1000, 0, 2), [123457, 'converted']),
  ('control: same month earlier', (99999, '2026-07-15', '2026-07-20', 100, 2, 2), [1000, 'converted']),
  ('control: after', (77, '2027-01-01', '2026-07-01', 10000, 2, 2), [77, 'native'])],
 [('regression cutover-boundary 1', (7192253, '2026-07-01', '2026-07-01', 10000, 2, 3), [7192253, 'native']),
  ('regression cutover-boundary 2', (12703023, '2026-07-15', '2026-07-15', 1000, 2, 2), [12703023, 'native']),
  ('partial repair guard 1', (25651605, '2026-07-03', '2026-07-20', 100, 0, 2), [25651605, 'converted']),
  ('partial repair guard 2', (25919315, '2026-07-02', '2026-07-15', 100, 2, 0), [2592, 'converted']),
  ('control: after', (77, '2027-01-01', '2026-07-01', 10000, 2, 2), [77, 'native']),
  ('control: small old amount', (4999, '2026-06-01', '2026-07-01', 10000, 2, 2), [0, 'converted']),
  ('control: before cutover', (1234567, '2026-06-30', '2026-07-01', 10000, 2, 2), [123, 'converted']),
  ('control: negative tie', (-15000, '2026-01-01', '2026-07-01', 10000, 2, 2), [-2, 'converted'])]]
for label, args, expected in cases[N - 1]:
    check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
regression cutover-boundary 1[0, 'converted'][500, 'native']Failed
regression cutover-boundary 2[7086273, 'converted'][7086273, 'native']Failed
partial repair guard 1[1000, 'converted'][1000, 'converted']Passed
partial repair guard 2[135910, 'converted'][135910, 'converted']Passed
control: before cutover[123, 'converted'][123, 'converted']Passed
control: negative tie[-2, 'converted'][-2, 'converted']Passed
control: exponent gain[123457, 'converted'][123457, 'converted']Passed
control: after[77, 'native'][77, 'native']Passed

SHA-256 / 5e8fbe299df725ad44787a959613941f125102a646bcfbe6d7130ae61672fd6a

2 / The unsuccessful fix

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json
from decimal import Decimal, ROUND_HALF_EVEN, ROUND_HALF_UP, ROUND_FLOOR, ROUND_CEILING, ROUND_DOWN, ROUND_UP, ROUND_HALF_DOWN
N = 1
observations = []
def solve(amount_minor, date, cutover, factor, old_exp, new_exp):
    if date[:7] >= cutover[:7]: return [amount_minor, 'native']
    v = Decimal(amount_minor) / factor * Decimal(10) ** (new_exp - old_exp)
    return [int(v.quantize(Decimal(1), rounding=ROUND_HALF_UP)), 'converted']
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
cases = [[('regression cutover-boundary 1', (500, '2026-07-01', '2026-07-01', 10000, 2, 2), [500, 'native']),
  ('regression cutover-boundary 2', (7086273, '2026-07-15', '2026-07-15', 1000, 0, 3), [7086273, 'native']),
  ('partial repair guard 1', (99999, '2026-07-15', '2026-07-20', 100, 2, 2), [1000, 'converted']),
  ('partial repair guard 2', (13591002, '2026-07-05', '2026-07-15', 1000, 2, 3), [135910, 'converted']),
  ('control: before cutover', (1234567, '2026-06-30', '2026-07-01', 10000, 2, 2), [123, 'converted']),
  ('control: negative tie', (-15000, '2026-01-01', '2026-07-01', 10000, 2, 2), [-2, 'converted']),
  ('control: exponent gain', (1234567, '2025-12-31', '2026-01-01', 1000, 0, 2), [123457, 'converted']),
  ('control: after', (77, '2027-01-01', '2026-07-01', 10000, 2, 2), [77, 'native'])],
 [('regression cutover-boundary 1', (23477950, '2026-07-01', '2026-07-01', 10000, 2, 3), [23477950, 'native']),
  ('regression cutover-boundary 2', (7192253, '2026-07-01', '2026-07-01', 10000, 2, 3), [7192253, 'native']),
  ('partial repair guard 1', (10748129, '2026-07-06', '2026-07-20', 10000, 0, 2), [107481, 'converted']),
  ('partial repair guard 2', (13003559, '2026-07-14', '2026-07-20', 10000, 2, 3), [13004, 'converted']),
  ('control: exponent gain', (1234567, '2025-12-31', '2026-01-01', 1000, 0, 2), [123457, 'converted']),
  ('control: same month earlier', (99999, '2026-07-15', '2026-07-20', 100, 2, 2), [1000, 'converted']),
  ('control: after', (77, '2027-01-01', '2026-07-01', 10000, 2, 2), [77, 'native']),
  ('control: small old amount', (4999, '2026-06-01', '2026-07-01', 10000, 2, 2), [0, 'converted'])],
 [('regression cutover-boundary 1', (12703023, '2026-07-15', '2026-07-15', 1000, 2, 2), [12703023, 'native']),
  ('regression cutover-boundary 2', (500, '2026-07-01', '2026-07-01', 10000, 2, 2), [500, 'native']),
  ('partial repair guard 1', (20559948, '2026-07-01', '2026-07-20', 10000, 0, 3), [2055995, 'converted']),
  ('partial repair guard 2', (9508148, '2026-07-07', '2026-07-20', 1000, 0, 3), [9508148, 'converted']),
  ('control: small old amount', (4999, '2026-06-01', '2026-07-01', 10000, 2, 2), [0, 'converted']),
  ('control: before cutover', (1234567, '2026-06-30', '2026-07-01', 10000, 2, 2), [123, 'converted']),
  ('control: negative tie', (-15000, '2026-01-01', '2026-07-01', 10000, 2, 2), [-2, 'converted']),
  ('control: exponent gain', (1234567, '2025-12-31', '2026-01-01', 1000, 0, 2), [123457, 'converted'])],
 [('regression cutover-boundary 1', (7086273, '2026-07-15', '2026-07-15', 1000, 0, 3), [7086273, 'native']),
  ('regression cutover-boundary 2', (23477950, '2026-07-01', '2026-07-01', 10000, 2, 3), [23477950, 'native']),
  ('partial repair guard 1', (1287988, '2026-07-12', '2026-07-15', 100, 0, 3), [12879880, 'converted']),
  ('partial repair guard 2', (20017420, '2026-07-14', '2026-07-15', 100, 0, 0), [200174, 'converted']),
  ('control: cutover day', (500, '2026-07-01', '2026-07-01', 10000, 2, 2), [500, 'native']),
  ('control: exponent gain', (1234567, '2025-12-31', '2026-01-01', 1000, 0, 2), [123457, 'converted']),
  ('control: same month earlier', (99999, '2026-07-15', '2026-07-20', 100, 2, 2), [1000, 'converted']),
  ('control: after', (77, '2027-01-01', '2026-07-01', 10000, 2, 2), [77, 'native'])],
 [('regression cutover-boundary 1', (7192253, '2026-07-01', '2026-07-01', 10000, 2, 3), [7192253, 'native']),
  ('regression cutover-boundary 2', (12703023, '2026-07-15', '2026-07-15', 1000, 2, 2), [12703023, 'native']),
  ('partial repair guard 1', (25651605, '2026-07-03', '2026-07-20', 100, 0, 2), [25651605, 'converted']),
  ('partial repair guard 2', (25919315, '2026-07-02', '2026-07-15', 100, 2, 0), [2592, 'converted']),
  ('control: after', (77, '2027-01-01', '2026-07-01', 10000, 2, 2), [77, 'native']),
  ('control: small old amount', (4999, '2026-06-01', '2026-07-01', 10000, 2, 2), [0, 'converted']),
  ('control: before cutover', (1234567, '2026-06-30', '2026-07-01', 10000, 2, 2), [123, 'converted']),
  ('control: negative tie', (-15000, '2026-01-01', '2026-07-01', 10000, 2, 2), [-2, 'converted'])]]
for label, args, expected in cases[N - 1]:
    check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
regression cutover-boundary 1[500, 'native'][500, 'native']Passed
regression cutover-boundary 2[7086273, 'native'][7086273, 'native']Passed
partial repair guard 1[99999, 'native'][1000, 'converted']Failed
partial repair guard 2[13591002, 'native'][135910, 'converted']Failed
control: before cutover[123, 'converted'][123, 'converted']Passed
control: negative tie[-2, 'converted'][-2, 'converted']Passed
control: exponent gain[123457, 'converted'][123457, 'converted']Passed
control: after[77, 'native'][77, 'native']Passed

SHA-256 / 5e382a5b4874a63002d46b242de4464a5cfaabc6c2ed213fff4f96857fb4b78b

3 / The verified repair

Exit 0
"""Failure Map reference implementation. Python standard library only."""
import json
from decimal import Decimal, ROUND_HALF_EVEN, ROUND_HALF_UP, ROUND_FLOOR, ROUND_CEILING, ROUND_DOWN, ROUND_UP, ROUND_HALF_DOWN
N = 1
observations = []
def solve(amount_minor, date, cutover, factor, old_exp, new_exp):
    if date >= cutover: return [amount_minor, 'native']
    v = Decimal(amount_minor) / factor * Decimal(10) ** (new_exp - old_exp)
    return [int(v.quantize(Decimal(1), rounding=ROUND_HALF_UP)), 'converted']
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
cases = [[('regression cutover-boundary 1', (500, '2026-07-01', '2026-07-01', 10000, 2, 2), [500, 'native']),
  ('regression cutover-boundary 2', (7086273, '2026-07-15', '2026-07-15', 1000, 0, 3), [7086273, 'native']),
  ('partial repair guard 1', (99999, '2026-07-15', '2026-07-20', 100, 2, 2), [1000, 'converted']),
  ('partial repair guard 2', (13591002, '2026-07-05', '2026-07-15', 1000, 2, 3), [135910, 'converted']),
  ('control: before cutover', (1234567, '2026-06-30', '2026-07-01', 10000, 2, 2), [123, 'converted']),
  ('control: negative tie', (-15000, '2026-01-01', '2026-07-01', 10000, 2, 2), [-2, 'converted']),
  ('control: exponent gain', (1234567, '2025-12-31', '2026-01-01', 1000, 0, 2), [123457, 'converted']),
  ('control: after', (77, '2027-01-01', '2026-07-01', 10000, 2, 2), [77, 'native'])],
 [('regression cutover-boundary 1', (23477950, '2026-07-01', '2026-07-01', 10000, 2, 3), [23477950, 'native']),
  ('regression cutover-boundary 2', (7192253, '2026-07-01', '2026-07-01', 10000, 2, 3), [7192253, 'native']),
  ('partial repair guard 1', (10748129, '2026-07-06', '2026-07-20', 10000, 0, 2), [107481, 'converted']),
  ('partial repair guard 2', (13003559, '2026-07-14', '2026-07-20', 10000, 2, 3), [13004, 'converted']),
  ('control: exponent gain', (1234567, '2025-12-31', '2026-01-01', 1000, 0, 2), [123457, 'converted']),
  ('control: same month earlier', (99999, '2026-07-15', '2026-07-20', 100, 2, 2), [1000, 'converted']),
  ('control: after', (77, '2027-01-01', '2026-07-01', 10000, 2, 2), [77, 'native']),
  ('control: small old amount', (4999, '2026-06-01', '2026-07-01', 10000, 2, 2), [0, 'converted'])],
 [('regression cutover-boundary 1', (12703023, '2026-07-15', '2026-07-15', 1000, 2, 2), [12703023, 'native']),
  ('regression cutover-boundary 2', (500, '2026-07-01', '2026-07-01', 10000, 2, 2), [500, 'native']),
  ('partial repair guard 1', (20559948, '2026-07-01', '2026-07-20', 10000, 0, 3), [2055995, 'converted']),
  ('partial repair guard 2', (9508148, '2026-07-07', '2026-07-20', 1000, 0, 3), [9508148, 'converted']),
  ('control: small old amount', (4999, '2026-06-01', '2026-07-01', 10000, 2, 2), [0, 'converted']),
  ('control: before cutover', (1234567, '2026-06-30', '2026-07-01', 10000, 2, 2), [123, 'converted']),
  ('control: negative tie', (-15000, '2026-01-01', '2026-07-01', 10000, 2, 2), [-2, 'converted']),
  ('control: exponent gain', (1234567, '2025-12-31', '2026-01-01', 1000, 0, 2), [123457, 'converted'])],
 [('regression cutover-boundary 1', (7086273, '2026-07-15', '2026-07-15', 1000, 0, 3), [7086273, 'native']),
  ('regression cutover-boundary 2', (23477950, '2026-07-01', '2026-07-01', 10000, 2, 3), [23477950, 'native']),
  ('partial repair guard 1', (1287988, '2026-07-12', '2026-07-15', 100, 0, 3), [12879880, 'converted']),
  ('partial repair guard 2', (20017420, '2026-07-14', '2026-07-15', 100, 0, 0), [200174, 'converted']),
  ('control: cutover day', (500, '2026-07-01', '2026-07-01', 10000, 2, 2), [500, 'native']),
  ('control: exponent gain', (1234567, '2025-12-31', '2026-01-01', 1000, 0, 2), [123457, 'converted']),
  ('control: same month earlier', (99999, '2026-07-15', '2026-07-20', 100, 2, 2), [1000, 'converted']),
  ('control: after', (77, '2027-01-01', '2026-07-01', 10000, 2, 2), [77, 'native'])],
 [('regression cutover-boundary 1', (7192253, '2026-07-01', '2026-07-01', 10000, 2, 3), [7192253, 'native']),
  ('regression cutover-boundary 2', (12703023, '2026-07-15', '2026-07-15', 1000, 2, 2), [12703023, 'native']),
  ('partial repair guard 1', (25651605, '2026-07-03', '2026-07-20', 100, 0, 2), [25651605, 'converted']),
  ('partial repair guard 2', (25919315, '2026-07-02', '2026-07-15', 100, 2, 0), [2592, 'converted']),
  ('control: after', (77, '2027-01-01', '2026-07-01', 10000, 2, 2), [77, 'native']),
  ('control: small old amount', (4999, '2026-06-01', '2026-07-01', 10000, 2, 2), [0, 'converted']),
  ('control: before cutover', (1234567, '2026-06-30', '2026-07-01', 10000, 2, 2), [123, 'converted']),
  ('control: negative tie', (-15000, '2026-01-01', '2026-07-01', 10000, 2, 2), [-2, 'converted'])]]
for label, args, expected in cases[N - 1]:
    check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
regression cutover-boundary 1[500, 'native'][500, 'native']Passed
regression cutover-boundary 2[7086273, 'native'][7086273, 'native']Passed
partial repair guard 1[1000, 'converted'][1000, 'converted']Passed
partial repair guard 2[135910, 'converted'][135910, 'converted']Passed
control: before cutover[123, 'converted'][123, 'converted']Passed
control: negative tie[-2, 'converted'][-2, 'converted']Passed
control: exponent gain[123457, 'converted'][123457, 'converted']Passed
control: after[77, 'native'][77, 'native']Passed

SHA-256 / 6c460e2904ea3def9cad97cb46b666bb2fa57be5991621779a321ec30159611f

Verification & scope

A deterministic, bounded teaching model with a stipulated toy contract; it makes no claim of conformance to any real regulation, standard, or institution's rules. This reproducer isolates one failure mechanism. Results cover the supplied fixtures. Variants within a family share a test contract and should remain grouped when constructing evaluation splits. Related mechanisms with a shared evaluation_group must also remain together; these controlled models are not independent production incidents.

Observations recorded using Python 3.12.14 at 2026-09-29T14:47:02.686720+00:00.

Case digest / d050b3190e51eabc1d8ec33e5d0682702eddc9e10534fc17596a66f828a99b66