FA-61841 / Currency rounding and FX conversion / Open access
Every currency is rounded to a hard-coded cent quantum · case 01
Yen amounts gain two fraction digits and dinar amounts lose their third minor digit.
ROOT CAUSE
The rounding quantum is the literal Decimal('0.01') instead of 10**-exponent for the resolved currency.
VERIFIED REPAIR
Quantize with Decimal(1).scaleb(-e) using the looked-up exponent.
Unsuccessful approach: Capping the exponent at two fixes zero-decimal currencies but still truncates three- and four-decimal currencies.
Case contract
solve(amount, ccy): amount is an exact decimal string, ccy an ISO-like code that is trimmed and upper-cased. Minor-unit exponents: USD 2, EUR 2, JPY 0, KRW 0, KWD 3, BHD 3, CLF 4. Round the exact decimal half-even at the currency exponent and return a plain string with exactly that many fraction digits; a zero result is unsigned. Unknown codes return 'ERR:unknown-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, ccy):
exps = {'USD': 2, 'EUR': 2, 'JPY': 0, 'KRW': 0, 'KWD': 3, 'BHD': 3, 'CLF': 4}
code = ccy.strip().upper()
if code not in exps: return 'ERR:unknown-currency'
e = exps[code]
x = Decimal(amount)
q = x.quantize(Decimal('0.01'), rounding=ROUND_HALF_EVEN)
if q == 0: q = abs(q)
return format(q, 'f')
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
cases = [[('regression quantum-per-currency 1', ('1235.5', 'JPY'), '1236'),
('regression quantum-per-currency 2', ('10.0005', 'KWD'), '10.000'),
('partial repair guard 2', ('1.23456', 'CLF'), '1.2346'), ('control: cents tie to even', ('2.665', 'USD'), '2.66'),
('control: lowercase code', ('12.345', ' eur '), '12.34'), ('control: negative small', ('-0.004', 'USD'), '0.00'),
('control: integer input', ('7', 'BHD'), '7.000')],
[('regression quantum-per-currency 1', ('1.23456', 'CLF'), '1.2346'),
('regression quantum-per-currency 2', ('7', 'BHD'), '7.000'),
('partial repair guard 2', ('37643.905', 'CLF'), '37643.9050'),
('control: lowercase code', ('12.345', ' eur '), '12.34'), ('control: negative small', ('-0.004', 'USD'), '0.00'),
('control: unknown code', ('1.00', 'ABC'), 'ERR:unknown-currency'),
('control: large amount', ('123456789.125', 'USD'), '123456789.12')],
[('regression quantum-per-currency 1', ('-2.5', 'KRW'), '-2'),
('regression quantum-per-currency 2', ('39257.444', 'KRW'), '39257'),
('partial repair guard 1', ('45179.821', 'CLF'), '45179.8210'),
('partial repair guard 2', ('10645.65286', 'Kwd '), '10645.653'), ('control: integer input', ('7', 'BHD'), '7.000'),
('control: unknown code', ('1.00', 'ABC'), 'ERR:unknown-currency'),
('control: large amount', ('123456789.125', 'USD'), '123456789.12'),
('control: cents tie to even', ('2.665', 'USD'), '2.66')],
[('regression quantum-per-currency 1', ('47295.38', ' jpy'), '47295'),
('regression quantum-per-currency 2', ('37643.905', 'CLF'), '37643.9050'),
('partial repair guard 1', ('-2451.34', 'KWD'), '-2451.340'),
('partial repair guard 2', ('19100.06047', 'BHD'), '19100.060'), ('control: negative tie', ('-2.5', 'KRW'), '-2'),
('control: cents tie to even', ('2.665', 'USD'), '2.66'), ('control: yen tie', ('1235.5', 'JPY'), '1236'),
('control: dinar three places', ('10.0005', 'KWD'), '10.000')],
[('regression quantum-per-currency 1', ('45179.821', 'CLF'), '45179.8210'),
('regression quantum-per-currency 2', ('4677.317', 'JPY'), '4677'),
('partial repair guard 1', ('16193.1040', 'Kwd '), '16193.104'),
('partial repair guard 2', ('32233.55636', 'Kwd '), '32233.556'),
('control: dinar three places', ('10.0005', 'KWD'), '10.000'),
('control: unit of account four places', ('1.23456', 'CLF'), '1.2346'),
('control: lowercase code', ('12.345', ' eur '), '12.34'), ('control: negative small', ('-0.004', 'USD'), '0.00')]]
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 quantum-per-currency 1 | 1235.50 | 1236 | Failed |
| regression quantum-per-currency 2 | 10.00 | 10.000 | Failed |
| partial repair guard 2 | 1.23 | 1.2346 | Failed |
| control: cents tie to even | 2.66 | 2.66 | Passed |
| control: lowercase code | 12.34 | 12.34 | Passed |
| control: negative small | 0.00 | 0.00 | Passed |
| control: integer input | 7.00 | 7.000 | Failed |
SHA-256 / 250319d14aa94b108d9e937a17010831386f74dd8d640526b28a021b6363e6c1
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, ccy):
exps = {'USD': 2, 'EUR': 2, 'JPY': 0, 'KRW': 0, 'KWD': 3, 'BHD': 3, 'CLF': 4}
code = ccy.strip().upper()
if code not in exps: return 'ERR:unknown-currency'
e = exps[code]
x = Decimal(amount)
q = x.quantize(Decimal(1).scaleb(-min(e, 2)), rounding=ROUND_HALF_EVEN)
if q == 0: q = abs(q)
return format(q, 'f')
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
cases = [[('regression quantum-per-currency 1', ('1235.5', 'JPY'), '1236'),
('regression quantum-per-currency 2', ('10.0005', 'KWD'), '10.000'),
('partial repair guard 2', ('1.23456', 'CLF'), '1.2346'), ('control: cents tie to even', ('2.665', 'USD'), '2.66'),
('control: lowercase code', ('12.345', ' eur '), '12.34'), ('control: negative small', ('-0.004', 'USD'), '0.00'),
('control: integer input', ('7', 'BHD'), '7.000')],
[('regression quantum-per-currency 1', ('1.23456', 'CLF'), '1.2346'),
('regression quantum-per-currency 2', ('7', 'BHD'), '7.000'),
('partial repair guard 2', ('37643.905', 'CLF'), '37643.9050'),
('control: lowercase code', ('12.345', ' eur '), '12.34'), ('control: negative small', ('-0.004', 'USD'), '0.00'),
('control: unknown code', ('1.00', 'ABC'), 'ERR:unknown-currency'),
('control: large amount', ('123456789.125', 'USD'), '123456789.12')],
[('regression quantum-per-currency 1', ('-2.5', 'KRW'), '-2'),
('regression quantum-per-currency 2', ('39257.444', 'KRW'), '39257'),
('partial repair guard 1', ('45179.821', 'CLF'), '45179.8210'),
('partial repair guard 2', ('10645.65286', 'Kwd '), '10645.653'), ('control: integer input', ('7', 'BHD'), '7.000'),
('control: unknown code', ('1.00', 'ABC'), 'ERR:unknown-currency'),
('control: large amount', ('123456789.125', 'USD'), '123456789.12'),
('control: cents tie to even', ('2.665', 'USD'), '2.66')],
[('regression quantum-per-currency 1', ('47295.38', ' jpy'), '47295'),
('regression quantum-per-currency 2', ('37643.905', 'CLF'), '37643.9050'),
('partial repair guard 1', ('-2451.34', 'KWD'), '-2451.340'),
('partial repair guard 2', ('19100.06047', 'BHD'), '19100.060'), ('control: negative tie', ('-2.5', 'KRW'), '-2'),
('control: cents tie to even', ('2.665', 'USD'), '2.66'), ('control: yen tie', ('1235.5', 'JPY'), '1236'),
('control: dinar three places', ('10.0005', 'KWD'), '10.000')],
[('regression quantum-per-currency 1', ('45179.821', 'CLF'), '45179.8210'),
('regression quantum-per-currency 2', ('4677.317', 'JPY'), '4677'),
('partial repair guard 1', ('16193.1040', 'Kwd '), '16193.104'),
('partial repair guard 2', ('32233.55636', 'Kwd '), '32233.556'),
('control: dinar three places', ('10.0005', 'KWD'), '10.000'),
('control: unit of account four places', ('1.23456', 'CLF'), '1.2346'),
('control: lowercase code', ('12.345', ' eur '), '12.34'), ('control: negative small', ('-0.004', 'USD'), '0.00')]]
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 quantum-per-currency 1 | 1236 | 1236 | Passed |
| regression quantum-per-currency 2 | 10.00 | 10.000 | Failed |
| partial repair guard 2 | 1.23 | 1.2346 | Failed |
| control: cents tie to even | 2.66 | 2.66 | Passed |
| control: lowercase code | 12.34 | 12.34 | Passed |
| control: negative small | 0.00 | 0.00 | Passed |
| control: integer input | 7.00 | 7.000 | Failed |
SHA-256 / ad76bff2ccaa41690a7504c957e4b1289e63c028c138998cf00ddc3510fc3c15
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, ccy):
exps = {'USD': 2, 'EUR': 2, 'JPY': 0, 'KRW': 0, 'KWD': 3, 'BHD': 3, 'CLF': 4}
code = ccy.strip().upper()
if code not in exps: return 'ERR:unknown-currency'
e = exps[code]
x = Decimal(amount)
q = x.quantize(Decimal(1).scaleb(-e), rounding=ROUND_HALF_EVEN)
if q == 0: q = abs(q)
return format(q, 'f')
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
cases = [[('regression quantum-per-currency 1', ('1235.5', 'JPY'), '1236'),
('regression quantum-per-currency 2', ('10.0005', 'KWD'), '10.000'),
('partial repair guard 2', ('1.23456', 'CLF'), '1.2346'), ('control: cents tie to even', ('2.665', 'USD'), '2.66'),
('control: lowercase code', ('12.345', ' eur '), '12.34'), ('control: negative small', ('-0.004', 'USD'), '0.00'),
('control: integer input', ('7', 'BHD'), '7.000')],
[('regression quantum-per-currency 1', ('1.23456', 'CLF'), '1.2346'),
('regression quantum-per-currency 2', ('7', 'BHD'), '7.000'),
('partial repair guard 2', ('37643.905', 'CLF'), '37643.9050'),
('control: lowercase code', ('12.345', ' eur '), '12.34'), ('control: negative small', ('-0.004', 'USD'), '0.00'),
('control: unknown code', ('1.00', 'ABC'), 'ERR:unknown-currency'),
('control: large amount', ('123456789.125', 'USD'), '123456789.12')],
[('regression quantum-per-currency 1', ('-2.5', 'KRW'), '-2'),
('regression quantum-per-currency 2', ('39257.444', 'KRW'), '39257'),
('partial repair guard 1', ('45179.821', 'CLF'), '45179.8210'),
('partial repair guard 2', ('10645.65286', 'Kwd '), '10645.653'), ('control: integer input', ('7', 'BHD'), '7.000'),
('control: unknown code', ('1.00', 'ABC'), 'ERR:unknown-currency'),
('control: large amount', ('123456789.125', 'USD'), '123456789.12'),
('control: cents tie to even', ('2.665', 'USD'), '2.66')],
[('regression quantum-per-currency 1', ('47295.38', ' jpy'), '47295'),
('regression quantum-per-currency 2', ('37643.905', 'CLF'), '37643.9050'),
('partial repair guard 1', ('-2451.34', 'KWD'), '-2451.340'),
('partial repair guard 2', ('19100.06047', 'BHD'), '19100.060'), ('control: negative tie', ('-2.5', 'KRW'), '-2'),
('control: cents tie to even', ('2.665', 'USD'), '2.66'), ('control: yen tie', ('1235.5', 'JPY'), '1236'),
('control: dinar three places', ('10.0005', 'KWD'), '10.000')],
[('regression quantum-per-currency 1', ('45179.821', 'CLF'), '45179.8210'),
('regression quantum-per-currency 2', ('4677.317', 'JPY'), '4677'),
('partial repair guard 1', ('16193.1040', 'Kwd '), '16193.104'),
('partial repair guard 2', ('32233.55636', 'Kwd '), '32233.556'),
('control: dinar three places', ('10.0005', 'KWD'), '10.000'),
('control: unit of account four places', ('1.23456', 'CLF'), '1.2346'),
('control: lowercase code', ('12.345', ' eur '), '12.34'), ('control: negative small', ('-0.004', 'USD'), '0.00')]]
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 quantum-per-currency 1 | 1236 | 1236 | Passed |
| regression quantum-per-currency 2 | 10.000 | 10.000 | Passed |
| partial repair guard 2 | 1.2346 | 1.2346 | Passed |
| control: cents tie to even | 2.66 | 2.66 | Passed |
| control: lowercase code | 12.34 | 12.34 | Passed |
| control: negative small | 0.00 | 0.00 | Passed |
| control: integer input | 7.000 | 7.000 | Passed |
SHA-256 / 5c0bca1f25673d61842729b6c5f078c2df1fb31ba51dff404540a1b385d21ffc
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:46:59.043817+00:00.
Case digest / 18252ddc678f38beaeae891014581106bf0a1d10f982b1080fe9e7219bb709fc