FA-61941 / Currency rounding and FX conversion / Open access
A same-currency conversion is routed through euro triangulation · case 01
Converting 12345 lira to lira returns 12346 lira.
ROOT CAUSE
The same-currency shortcut is missing, so the amount is triangulated through a rounded EUR intermediate.
VERIFIED REPAIR
When source and target match, only round the amount to the currency exponent.
Unsuccessful approach: Returning the input string unchanged skips the required rounding to the currency exponent.
Case contract
solve(amount, frm, to): stipulated legacy-currency triangulation. Fixed rates are units per 1 EUR (DEM 1.95583, FRF 6.55957, ITL 1936.27, ESP 166.386, IEP 0.787564, NLG 2.20371, BEF 40.3399, PTE 200.482, ATS 13.7603, FIM 5.94573). Exponents: ITL, ESP, BEF, PTE 0; others and EUR 2. Legacy to legacy: divide by the source rate, round the EUR intermediate half-up to 3 decimals, multiply by the target rate, round half-up to the target exponent. Legacy to EUR: divide and round half-up to 2 decimals. EUR to legacy: multiply and round half-up to the target exponent. Same currency: round the amount half-up to its exponent. Inverse rates are never used. Unknown codes return 'ERR:currency'.
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, frm, to):
rates = {'DEM': '1.95583', 'FRF': '6.55957', 'ITL': '1936.27', 'ESP': '166.386', 'IEP': '0.787564', 'NLG': '2.20371', 'BEF': '40.3399', 'PTE': '200.482', 'ATS': '13.7603', 'FIM': '5.94573', 'EUR': '1'}
zero = ('ITL', 'ESP', 'BEF', 'PTE')
if frm not in rates or to not in rates: return 'ERR:currency'
def q(v, c):
return v.quantize(Decimal(1) if c in zero else Decimal('0.01'), rounding=ROUND_HALF_UP)
x = Decimal(amount)
pass
if frm == 'EUR':
eur = x
elif to == 'EUR':
return format(q(x / Decimal(rates[frm]), 'EUR'), 'f')
else:
eur = (x / Decimal(rates[frm])).quantize(Decimal('0.001'), rounding=ROUND_HALF_UP)
return format(q(eur * Decimal(rates[to]), to), 'f')
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
cases = [[('regression same-currency 1', ('12345', 'ITL', 'ITL'), '12345'),
('regression same-currency 2', ('409108', 'ITL', 'ITL'), '409108'),
('partial repair guard 1', ('100.5', 'ITL', 'ITL'), '101'),
('partial repair guard 2', ('1.005', 'DEM', 'DEM'), '1.01'),
('control: mark to franc', ('100', 'DEM', 'FRF'), '335.38'),
('control: lira to euro', ('1000000', 'ITL', 'EUR'), '516.46'),
('control: euro to lira', ('10', 'EUR', 'ITL'), '19363'), ('control: unknown', ('5', 'XEU', 'DEM'), 'ERR:currency')],
[('regression same-currency 1', ('12345', 'ITL', 'ITL'), '12345'),
('regression same-currency 2', ('409108', 'ITL', 'ITL'), '409108'),
('partial repair guard 1', ('278015', 'FIM', 'FIM'), '278015.00'),
('partial repair guard 2', ('162491', 'FRF', 'FRF'), '162491.00'),
('control: same currency rounding', ('100.5', 'ITL', 'ITL'), '101'),
('control: unknown', ('5', 'XEU', 'DEM'), 'ERR:currency'),
('control: peseta to punt', ('123456', 'ESP', 'IEP'), '584.36'),
('control: guilder to escudo', ('0.01', 'NLG', 'PTE'), '1')],
[('regression same-currency 1', ('12345', 'ITL', 'ITL'), '12345'),
('regression same-currency 2', ('409108', 'ITL', 'ITL'), '409108'),
('partial repair guard 1', ('3161904', 'FRF', 'FRF'), '3161904.00'),
('partial repair guard 2', ('604787', 'EUR', 'EUR'), '604787.00'),
('control: guilder to escudo', ('0.01', 'NLG', 'PTE'), '1'),
('control: schilling to markka', ('987.65', 'ATS', 'FIM'), '426.75'),
('control: franc to franc', ('12.345', 'BEF', 'FRF'), '2.01'),
('control: mark to franc', ('100', 'DEM', 'FRF'), '335.38')],
[('regression same-currency 1', ('12345', 'ITL', 'ITL'), '12345'),
('regression same-currency 2', ('409108', 'ITL', 'ITL'), '409108'),
('partial repair guard 1', ('2390508', 'EUR', 'EUR'), '2390508.00'),
('partial repair guard 2', ('4901642', 'IEP', 'IEP'), '4901642.00'),
('control: mark to franc', ('100', 'DEM', 'FRF'), '335.38'),
('control: lira to euro', ('1000000', 'ITL', 'EUR'), '516.46'),
('control: euro to lira', ('10', 'EUR', 'ITL'), '19363'),
('control: same currency rounding', ('100.5', 'ITL', 'ITL'), '101')],
[('regression same-currency 1', ('12345', 'ITL', 'ITL'), '12345'),
('regression same-currency 2', ('409108', 'ITL', 'ITL'), '409108'),
('partial repair guard 1', ('3675688', 'EUR', 'EUR'), '3675688.00'),
('partial repair guard 2', ('4983100', 'DEM', 'DEM'), '4983100.00'),
('control: same currency rounding', ('100.5', 'ITL', 'ITL'), '101'),
('control: unknown', ('5', 'XEU', 'DEM'), 'ERR:currency'),
('control: peseta to punt', ('123456', 'ESP', 'IEP'), '584.36'),
('control: guilder to escudo', ('0.01', 'NLG', 'PTE'), '1')]]
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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| regression same-currency 1 | 12346 | 12345 | Failed |
| regression same-currency 2 | 409109 | 409108 | Failed |
| partial repair guard 1 | 101 | 101 | Passed |
| partial repair guard 2 | 1.01 | 1.01 | Passed |
| control: mark to franc | 335.38 | 335.38 | Passed |
| control: lira to euro | 516.46 | 516.46 | Passed |
| control: euro to lira | 19363 | 19363 | Passed |
| control: unknown | ERR:currency | ERR:currency | Passed |
SHA-256 / 64ecb0526c292905e892945e51ff33d45365967cdeab4fe7b6a9f40c99561bd4
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, frm, to):
rates = {'DEM': '1.95583', 'FRF': '6.55957', 'ITL': '1936.27', 'ESP': '166.386', 'IEP': '0.787564', 'NLG': '2.20371', 'BEF': '40.3399', 'PTE': '200.482', 'ATS': '13.7603', 'FIM': '5.94573', 'EUR': '1'}
zero = ('ITL', 'ESP', 'BEF', 'PTE')
if frm not in rates or to not in rates: return 'ERR:currency'
def q(v, c):
return v.quantize(Decimal(1) if c in zero else Decimal('0.01'), rounding=ROUND_HALF_UP)
x = Decimal(amount)
if frm == to: return amount
if frm == 'EUR':
eur = x
elif to == 'EUR':
return format(q(x / Decimal(rates[frm]), 'EUR'), 'f')
else:
eur = (x / Decimal(rates[frm])).quantize(Decimal('0.001'), rounding=ROUND_HALF_UP)
return format(q(eur * Decimal(rates[to]), to), 'f')
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
cases = [[('regression same-currency 1', ('12345', 'ITL', 'ITL'), '12345'),
('regression same-currency 2', ('409108', 'ITL', 'ITL'), '409108'),
('partial repair guard 1', ('100.5', 'ITL', 'ITL'), '101'),
('partial repair guard 2', ('1.005', 'DEM', 'DEM'), '1.01'),
('control: mark to franc', ('100', 'DEM', 'FRF'), '335.38'),
('control: lira to euro', ('1000000', 'ITL', 'EUR'), '516.46'),
('control: euro to lira', ('10', 'EUR', 'ITL'), '19363'), ('control: unknown', ('5', 'XEU', 'DEM'), 'ERR:currency')],
[('regression same-currency 1', ('12345', 'ITL', 'ITL'), '12345'),
('regression same-currency 2', ('409108', 'ITL', 'ITL'), '409108'),
('partial repair guard 1', ('278015', 'FIM', 'FIM'), '278015.00'),
('partial repair guard 2', ('162491', 'FRF', 'FRF'), '162491.00'),
('control: same currency rounding', ('100.5', 'ITL', 'ITL'), '101'),
('control: unknown', ('5', 'XEU', 'DEM'), 'ERR:currency'),
('control: peseta to punt', ('123456', 'ESP', 'IEP'), '584.36'),
('control: guilder to escudo', ('0.01', 'NLG', 'PTE'), '1')],
[('regression same-currency 1', ('12345', 'ITL', 'ITL'), '12345'),
('regression same-currency 2', ('409108', 'ITL', 'ITL'), '409108'),
('partial repair guard 1', ('3161904', 'FRF', 'FRF'), '3161904.00'),
('partial repair guard 2', ('604787', 'EUR', 'EUR'), '604787.00'),
('control: guilder to escudo', ('0.01', 'NLG', 'PTE'), '1'),
('control: schilling to markka', ('987.65', 'ATS', 'FIM'), '426.75'),
('control: franc to franc', ('12.345', 'BEF', 'FRF'), '2.01'),
('control: mark to franc', ('100', 'DEM', 'FRF'), '335.38')],
[('regression same-currency 1', ('12345', 'ITL', 'ITL'), '12345'),
('regression same-currency 2', ('409108', 'ITL', 'ITL'), '409108'),
('partial repair guard 1', ('2390508', 'EUR', 'EUR'), '2390508.00'),
('partial repair guard 2', ('4901642', 'IEP', 'IEP'), '4901642.00'),
('control: mark to franc', ('100', 'DEM', 'FRF'), '335.38'),
('control: lira to euro', ('1000000', 'ITL', 'EUR'), '516.46'),
('control: euro to lira', ('10', 'EUR', 'ITL'), '19363'),
('control: same currency rounding', ('100.5', 'ITL', 'ITL'), '101')],
[('regression same-currency 1', ('12345', 'ITL', 'ITL'), '12345'),
('regression same-currency 2', ('409108', 'ITL', 'ITL'), '409108'),
('partial repair guard 1', ('3675688', 'EUR', 'EUR'), '3675688.00'),
('partial repair guard 2', ('4983100', 'DEM', 'DEM'), '4983100.00'),
('control: same currency rounding', ('100.5', 'ITL', 'ITL'), '101'),
('control: unknown', ('5', 'XEU', 'DEM'), 'ERR:currency'),
('control: peseta to punt', ('123456', 'ESP', 'IEP'), '584.36'),
('control: guilder to escudo', ('0.01', 'NLG', 'PTE'), '1')]]
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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| regression same-currency 1 | 12345 | 12345 | Passed |
| regression same-currency 2 | 409108 | 409108 | Passed |
| partial repair guard 1 | 100.5 | 101 | Failed |
| partial repair guard 2 | 1.005 | 1.01 | Failed |
| control: mark to franc | 335.38 | 335.38 | Passed |
| control: lira to euro | 516.46 | 516.46 | Passed |
| control: euro to lira | 19363 | 19363 | Passed |
| control: unknown | ERR:currency | ERR:currency | Passed |
SHA-256 / cb3d43ff5b1e9237795c3daf613456543c2708eeb4fb29a93b321d0d4e2db778
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, frm, to):
rates = {'DEM': '1.95583', 'FRF': '6.55957', 'ITL': '1936.27', 'ESP': '166.386', 'IEP': '0.787564', 'NLG': '2.20371', 'BEF': '40.3399', 'PTE': '200.482', 'ATS': '13.7603', 'FIM': '5.94573', 'EUR': '1'}
zero = ('ITL', 'ESP', 'BEF', 'PTE')
if frm not in rates or to not in rates: return 'ERR:currency'
def q(v, c):
return v.quantize(Decimal(1) if c in zero else Decimal('0.01'), rounding=ROUND_HALF_UP)
x = Decimal(amount)
if frm == to: return format(q(x, to), 'f')
if frm == 'EUR':
eur = x
elif to == 'EUR':
return format(q(x / Decimal(rates[frm]), 'EUR'), 'f')
else:
eur = (x / Decimal(rates[frm])).quantize(Decimal('0.001'), rounding=ROUND_HALF_UP)
return format(q(eur * Decimal(rates[to]), to), 'f')
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
cases = [[('regression same-currency 1', ('12345', 'ITL', 'ITL'), '12345'),
('regression same-currency 2', ('409108', 'ITL', 'ITL'), '409108'),
('partial repair guard 1', ('100.5', 'ITL', 'ITL'), '101'),
('partial repair guard 2', ('1.005', 'DEM', 'DEM'), '1.01'),
('control: mark to franc', ('100', 'DEM', 'FRF'), '335.38'),
('control: lira to euro', ('1000000', 'ITL', 'EUR'), '516.46'),
('control: euro to lira', ('10', 'EUR', 'ITL'), '19363'), ('control: unknown', ('5', 'XEU', 'DEM'), 'ERR:currency')],
[('regression same-currency 1', ('12345', 'ITL', 'ITL'), '12345'),
('regression same-currency 2', ('409108', 'ITL', 'ITL'), '409108'),
('partial repair guard 1', ('278015', 'FIM', 'FIM'), '278015.00'),
('partial repair guard 2', ('162491', 'FRF', 'FRF'), '162491.00'),
('control: same currency rounding', ('100.5', 'ITL', 'ITL'), '101'),
('control: unknown', ('5', 'XEU', 'DEM'), 'ERR:currency'),
('control: peseta to punt', ('123456', 'ESP', 'IEP'), '584.36'),
('control: guilder to escudo', ('0.01', 'NLG', 'PTE'), '1')],
[('regression same-currency 1', ('12345', 'ITL', 'ITL'), '12345'),
('regression same-currency 2', ('409108', 'ITL', 'ITL'), '409108'),
('partial repair guard 1', ('3161904', 'FRF', 'FRF'), '3161904.00'),
('partial repair guard 2', ('604787', 'EUR', 'EUR'), '604787.00'),
('control: guilder to escudo', ('0.01', 'NLG', 'PTE'), '1'),
('control: schilling to markka', ('987.65', 'ATS', 'FIM'), '426.75'),
('control: franc to franc', ('12.345', 'BEF', 'FRF'), '2.01'),
('control: mark to franc', ('100', 'DEM', 'FRF'), '335.38')],
[('regression same-currency 1', ('12345', 'ITL', 'ITL'), '12345'),
('regression same-currency 2', ('409108', 'ITL', 'ITL'), '409108'),
('partial repair guard 1', ('2390508', 'EUR', 'EUR'), '2390508.00'),
('partial repair guard 2', ('4901642', 'IEP', 'IEP'), '4901642.00'),
('control: mark to franc', ('100', 'DEM', 'FRF'), '335.38'),
('control: lira to euro', ('1000000', 'ITL', 'EUR'), '516.46'),
('control: euro to lira', ('10', 'EUR', 'ITL'), '19363'),
('control: same currency rounding', ('100.5', 'ITL', 'ITL'), '101')],
[('regression same-currency 1', ('12345', 'ITL', 'ITL'), '12345'),
('regression same-currency 2', ('409108', 'ITL', 'ITL'), '409108'),
('partial repair guard 1', ('3675688', 'EUR', 'EUR'), '3675688.00'),
('partial repair guard 2', ('4983100', 'DEM', 'DEM'), '4983100.00'),
('control: same currency rounding', ('100.5', 'ITL', 'ITL'), '101'),
('control: unknown', ('5', 'XEU', 'DEM'), 'ERR:currency'),
('control: peseta to punt', ('123456', 'ESP', 'IEP'), '584.36'),
('control: guilder to escudo', ('0.01', 'NLG', 'PTE'), '1')]]
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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| regression same-currency 1 | 12345 | 12345 | Passed |
| regression same-currency 2 | 409108 | 409108 | Passed |
| partial repair guard 1 | 101 | 101 | Passed |
| partial repair guard 2 | 1.01 | 1.01 | Passed |
| control: mark to franc | 335.38 | 335.38 | Passed |
| control: lira to euro | 516.46 | 516.46 | Passed |
| control: euro to lira | 19363 | 19363 | Passed |
| control: unknown | ERR:currency | ERR:currency | Passed |
SHA-256 / 0067e6ba2dfc745ed7653acb9c5dca8857c4b4c520a5b7846298e24c0f2f839a
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:00.023870+00:00.
Case digest / 247fcfbc443261def8ac6a2c5215dc4cea308691aa3dc90a81d901bb82d3b1b3