FAILURE MAP
← Case archive

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

Digit groups are cut from the left · case 01

1234 dollars renders as '$123,4.00'.

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

ROOT CAUSE

The whole part is chunked into threes starting at the most significant digit.

VERIFIED REPAIR

Group from the right.

Unsuccessful approach: Zero-padding to a multiple of three and stripping leading zeros loses a zero whole part.

Case contract

solve(minor, ccy): format integer minor units. Styles: USD '$' prefix, ',' groups, '.' decimal, 2 digits; EUR ' €' suffix, '.' groups, ',' decimal, 2; JPY '¥' prefix, ',' groups, 0 digits (no decimal separator); CHF 'CHF ' prefix, "'" groups, '.', 2; KWD 'KD ' prefix, ',', '.', 3. Group the whole part in threes from the right; the fraction is zero-padded to the exponent. A negative amount places '-' before everything. 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

N = 1
observations = []
def solve(minor, ccy):
    spec = {'USD': ('$', '', ',', '.', 2), 'EUR': ('', ' €', '.', ',', 2), 'JPY': ('¥', '', ',', '.', 0), 'CHF': ('CHF ', '', "'", '.', 2), 'KWD': ('KD ', '', ',', '.', 3)}
    if ccy not in spec: return 'ERR:currency'
    pre, suf, grp, dec, e = spec[ccy]
    neg = minor < 0
    major, frac = divmod(abs(minor), 10 ** e)
    digits = str(major)
    groups = [digits[i:i + 3] for i in range(0, len(digits), 3)]
    body = grp.join(groups)
    if e: body += dec + str(frac).zfill(e)
    return ('-' if neg else '') + pre + body + suf
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
cases = [[('regression grouping-direction 1', (123450, 'USD'), '$1,234.50'),
  ('regression grouping-direction 2', (1234567, 'JPY'), '¥1,234,567'),
  ('partial repair guard 1', (-50, 'EUR'), '-0,50 €'), ('partial repair guard 2', (0, 'JPY'), '¥0'),
  ('control: negative dollars', (-150, 'USD'), '-$1.50'), ('control: franc', (12345678, 'CHF'), "CHF 123'456.78"),
  ('control: dinar', (1005, 'KWD'), 'KD 1.005'), ('control: unknown', (5, 'GBP'), 'ERR:currency')],
 [('regression grouping-direction 1', (100000000, 'EUR'), '1.000.000,00 €'),
  ('regression grouping-direction 2', (-1000, 'JPY'), '-¥1,000'), ('partial repair guard 1', (999, 'KWD'), 'KD 0.999'),
  ('partial repair guard 2', (0, 'CHF'), 'CHF 0.00'), ('control: yen', (1234567, 'JPY'), '¥1,234,567'),
  ('control: franc', (12345678, 'CHF'), "CHF 123'456.78"), ('control: dinar', (1005, 'KWD'), 'KD 1.005'),
  ('control: unknown', (5, 'GBP'), 'ERR:currency')],
 [('regression grouping-direction 1', (-1000000, 'EUR'), '-10.000,00 €'),
  ('regression grouping-direction 2', (987654321, 'EUR'), '9.876.543,21 €'),
  ('partial repair guard 1', (-50, 'KWD'), '-KD 0.050'), ('partial repair guard 2', (5, 'CHF'), 'CHF 0.05'),
  ('control: unknown', (5, 'GBP'), 'ERR:currency'), ('control: million euros', (100000000, 'EUR'), '1.000.000,00 €'),
  ('control: zero yen', (0, 'JPY'), '¥0'), ('control: small dollars', (105, 'USD'), '$1.05')],
 [('regression grouping-direction 1', (-100000, 'USD'), '-$1,000.00'),
  ('regression grouping-direction 2', (99999, 'JPY'), '¥99,999'),
  ('partial repair guard 1', (-105, 'KWD'), '-KD 0.105'), ('partial repair guard 2', (50, 'EUR'), '0,50 €'),
  ('control: small dollars', (105, 'USD'), '$1.05'), ('control: negative yen', (-1000, 'JPY'), '-¥1,000'),
  ('control: dollars', (123450, 'USD'), '$1,234.50'), ('control: negative dollars', (-150, 'USD'), '-$1.50')],
 [('regression grouping-direction 1', (123456, 'USD'), '$1,234.56'),
  ('regression grouping-direction 2', (-123456, 'EUR'), '-1.234,56 €'),
  ('partial repair guard 1', (0, 'EUR'), '0,00 €'), ('partial repair guard 2', (50, 'USD'), '$0.50'),
  ('control: negative dollars', (-150, 'USD'), '-$1.50'), ('control: negative euro cents', (-50, 'EUR'), '-0,50 €'),
  ('control: yen', (1234567, 'JPY'), '¥1,234,567'), ('control: franc', (12345678, 'CHF'), "CHF 123'456.78")]]
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 grouping-direction 1$123,4.50$1,234.50Failed
regression grouping-direction 2¥123,456,7¥1,234,567Failed
partial repair guard 1-0,50 €-0,50 €Passed
partial repair guard 2¥0¥0Passed
control: negative dollars-$1.50-$1.50Passed
control: francCHF 123'456.78CHF 123'456.78Passed
control: dinarKD 1.005KD 1.005Passed
control: unknownERR:currencyERR:currencyPassed

SHA-256 / 71535cf3dabcbf5f69737a3f66b8498ace0a4053652a181ff585635e25202ad9

2 / The unsuccessful fix

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(minor, ccy):
    spec = {'USD': ('$', '', ',', '.', 2), 'EUR': ('', ' €', '.', ',', 2), 'JPY': ('¥', '', ',', '.', 0), 'CHF': ('CHF ', '', "'", '.', 2), 'KWD': ('KD ', '', ',', '.', 3)}
    if ccy not in spec: return 'ERR:currency'
    pre, suf, grp, dec, e = spec[ccy]
    neg = minor < 0
    major, frac = divmod(abs(minor), 10 ** e)
    digits = str(major)
    d = digits.zfill((len(digits) + 2) // 3 * 3)
    groups = [d[i:i + 3] for i in range(0, len(d), 3)]
    groups[0] = groups[0].lstrip('0')
    body = grp.join(groups)
    if e: body += dec + str(frac).zfill(e)
    return ('-' if neg else '') + pre + body + suf
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
cases = [[('regression grouping-direction 1', (123450, 'USD'), '$1,234.50'),
  ('regression grouping-direction 2', (1234567, 'JPY'), '¥1,234,567'),
  ('partial repair guard 1', (-50, 'EUR'), '-0,50 €'), ('partial repair guard 2', (0, 'JPY'), '¥0'),
  ('control: negative dollars', (-150, 'USD'), '-$1.50'), ('control: franc', (12345678, 'CHF'), "CHF 123'456.78"),
  ('control: dinar', (1005, 'KWD'), 'KD 1.005'), ('control: unknown', (5, 'GBP'), 'ERR:currency')],
 [('regression grouping-direction 1', (100000000, 'EUR'), '1.000.000,00 €'),
  ('regression grouping-direction 2', (-1000, 'JPY'), '-¥1,000'), ('partial repair guard 1', (999, 'KWD'), 'KD 0.999'),
  ('partial repair guard 2', (0, 'CHF'), 'CHF 0.00'), ('control: yen', (1234567, 'JPY'), '¥1,234,567'),
  ('control: franc', (12345678, 'CHF'), "CHF 123'456.78"), ('control: dinar', (1005, 'KWD'), 'KD 1.005'),
  ('control: unknown', (5, 'GBP'), 'ERR:currency')],
 [('regression grouping-direction 1', (-1000000, 'EUR'), '-10.000,00 €'),
  ('regression grouping-direction 2', (987654321, 'EUR'), '9.876.543,21 €'),
  ('partial repair guard 1', (-50, 'KWD'), '-KD 0.050'), ('partial repair guard 2', (5, 'CHF'), 'CHF 0.05'),
  ('control: unknown', (5, 'GBP'), 'ERR:currency'), ('control: million euros', (100000000, 'EUR'), '1.000.000,00 €'),
  ('control: zero yen', (0, 'JPY'), '¥0'), ('control: small dollars', (105, 'USD'), '$1.05')],
 [('regression grouping-direction 1', (-100000, 'USD'), '-$1,000.00'),
  ('regression grouping-direction 2', (99999, 'JPY'), '¥99,999'),
  ('partial repair guard 1', (-105, 'KWD'), '-KD 0.105'), ('partial repair guard 2', (50, 'EUR'), '0,50 €'),
  ('control: small dollars', (105, 'USD'), '$1.05'), ('control: negative yen', (-1000, 'JPY'), '-¥1,000'),
  ('control: dollars', (123450, 'USD'), '$1,234.50'), ('control: negative dollars', (-150, 'USD'), '-$1.50')],
 [('regression grouping-direction 1', (123456, 'USD'), '$1,234.56'),
  ('regression grouping-direction 2', (-123456, 'EUR'), '-1.234,56 €'),
  ('partial repair guard 1', (0, 'EUR'), '0,00 €'), ('partial repair guard 2', (50, 'USD'), '$0.50'),
  ('control: negative dollars', (-150, 'USD'), '-$1.50'), ('control: negative euro cents', (-50, 'EUR'), '-0,50 €'),
  ('control: yen', (1234567, 'JPY'), '¥1,234,567'), ('control: franc', (12345678, 'CHF'), "CHF 123'456.78")]]
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 grouping-direction 1$1,234.50$1,234.50Passed
regression grouping-direction 2¥1,234,567¥1,234,567Passed
partial repair guard 1-,50 €-0,50 €Failed
partial repair guard 2¥¥0Failed
control: negative dollars-$1.50-$1.50Passed
control: francCHF 123'456.78CHF 123'456.78Passed
control: dinarKD 1.005KD 1.005Passed
control: unknownERR:currencyERR:currencyPassed

SHA-256 / 86c0c72722c822977f28c9d4c7b6bbe64023bc960ca4a8f56a65e94785039362

3 / The verified repair

Exit 0
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(minor, ccy):
    spec = {'USD': ('$', '', ',', '.', 2), 'EUR': ('', ' €', '.', ',', 2), 'JPY': ('¥', '', ',', '.', 0), 'CHF': ('CHF ', '', "'", '.', 2), 'KWD': ('KD ', '', ',', '.', 3)}
    if ccy not in spec: return 'ERR:currency'
    pre, suf, grp, dec, e = spec[ccy]
    neg = minor < 0
    major, frac = divmod(abs(minor), 10 ** e)
    digits = str(major)
    groups = []
    while len(digits) > 3:
        groups.insert(0, digits[-3:])
        digits = digits[:-3]
    groups.insert(0, digits)
    body = grp.join(groups)
    if e: body += dec + str(frac).zfill(e)
    return ('-' if neg else '') + pre + body + suf
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
cases = [[('regression grouping-direction 1', (123450, 'USD'), '$1,234.50'),
  ('regression grouping-direction 2', (1234567, 'JPY'), '¥1,234,567'),
  ('partial repair guard 1', (-50, 'EUR'), '-0,50 €'), ('partial repair guard 2', (0, 'JPY'), '¥0'),
  ('control: negative dollars', (-150, 'USD'), '-$1.50'), ('control: franc', (12345678, 'CHF'), "CHF 123'456.78"),
  ('control: dinar', (1005, 'KWD'), 'KD 1.005'), ('control: unknown', (5, 'GBP'), 'ERR:currency')],
 [('regression grouping-direction 1', (100000000, 'EUR'), '1.000.000,00 €'),
  ('regression grouping-direction 2', (-1000, 'JPY'), '-¥1,000'), ('partial repair guard 1', (999, 'KWD'), 'KD 0.999'),
  ('partial repair guard 2', (0, 'CHF'), 'CHF 0.00'), ('control: yen', (1234567, 'JPY'), '¥1,234,567'),
  ('control: franc', (12345678, 'CHF'), "CHF 123'456.78"), ('control: dinar', (1005, 'KWD'), 'KD 1.005'),
  ('control: unknown', (5, 'GBP'), 'ERR:currency')],
 [('regression grouping-direction 1', (-1000000, 'EUR'), '-10.000,00 €'),
  ('regression grouping-direction 2', (987654321, 'EUR'), '9.876.543,21 €'),
  ('partial repair guard 1', (-50, 'KWD'), '-KD 0.050'), ('partial repair guard 2', (5, 'CHF'), 'CHF 0.05'),
  ('control: unknown', (5, 'GBP'), 'ERR:currency'), ('control: million euros', (100000000, 'EUR'), '1.000.000,00 €'),
  ('control: zero yen', (0, 'JPY'), '¥0'), ('control: small dollars', (105, 'USD'), '$1.05')],
 [('regression grouping-direction 1', (-100000, 'USD'), '-$1,000.00'),
  ('regression grouping-direction 2', (99999, 'JPY'), '¥99,999'),
  ('partial repair guard 1', (-105, 'KWD'), '-KD 0.105'), ('partial repair guard 2', (50, 'EUR'), '0,50 €'),
  ('control: small dollars', (105, 'USD'), '$1.05'), ('control: negative yen', (-1000, 'JPY'), '-¥1,000'),
  ('control: dollars', (123450, 'USD'), '$1,234.50'), ('control: negative dollars', (-150, 'USD'), '-$1.50')],
 [('regression grouping-direction 1', (123456, 'USD'), '$1,234.56'),
  ('regression grouping-direction 2', (-123456, 'EUR'), '-1.234,56 €'),
  ('partial repair guard 1', (0, 'EUR'), '0,00 €'), ('partial repair guard 2', (50, 'USD'), '$0.50'),
  ('control: negative dollars', (-150, 'USD'), '-$1.50'), ('control: negative euro cents', (-50, 'EUR'), '-0,50 €'),
  ('control: yen', (1234567, 'JPY'), '¥1,234,567'), ('control: franc', (12345678, 'CHF'), "CHF 123'456.78")]]
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 grouping-direction 1$1,234.50$1,234.50Passed
regression grouping-direction 2¥1,234,567¥1,234,567Passed
partial repair guard 1-0,50 €-0,50 €Passed
partial repair guard 2¥0¥0Passed
control: negative dollars-$1.50-$1.50Passed
control: francCHF 123'456.78CHF 123'456.78Passed
control: dinarKD 1.005KD 1.005Passed
control: unknownERR:currencyERR:currencyPassed

SHA-256 / af52db0ee1c09db4442e4da7509a18c938cee3c9e31f7f41f35a9d3bf3cdd85e

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:01.985666+00:00.

Case digest / 4356ab11dedbc5bf5cc268f10b1dcb7d39d51de4f404f7e2df674a3c8e3cd3e3