FAILURE MAP
← Case archive

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

Minor units are converted without rescaling between currency exponents · case 01

1000 US cents convert to 151370 yen instead of 1514 yen.

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

ROOT CAUSE

The factor 10**(to_exp - from_exp) is omitted when converting minor units.

VERIFIED REPAIR

Multiply by 10**(to_exp - from_exp) to move between minor-unit scales.

Unsuccessful approach: Rescaling by 10**(from_exp - to_exp) inverts the adjustment.

Case contract

solve(amount_minor, from_exp, to_exp, rate): amount_minor is an integer count of source minor units, rate a decimal string giving target major units per source major unit. The target minor amount is amount_minor * rate * 10**(to_exp - from_exp), computed exactly and rounded half-even to an integer.

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, from_exp, to_exp, rate):
    scale = Decimal(1)
    v = Decimal(amount_minor) * Decimal(rate) * scale
    return int(v.quantize(Decimal(1), rounding=ROUND_HALF_EVEN))
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
cases = [[('regression exponent-rescale 1', (1000, 2, 0, '151.37'), 1514),
  ('regression exponent-rescale 2', (1, 0, 3, '0.3071'), 307), ('control: float trap tie', (115, 2, 2, '1.1'), 126),
  ('control: negative exact', (-250, 2, 2, '0.5'), -125), ('control: negative tie', (-3, 2, 2, '0.5'), -2),
  ('control: zero', (0, 2, 0, '151.37'), 0)],
 [('regression exponent-rescale 1', (987654321, 2, 3, '0.30712'), 3033283951),
  ('regression exponent-rescale 2', (64824, 0, 2, '51.9345'), 336660203),
  ('control: yen to dinar', (1, 0, 3, '0.3071'), 307), ('control: negative tie', (-3, 2, 2, '0.5'), -2),
  ('control: zero', (0, 2, 0, '151.37'), 0), ('control: precise rate', (123456, 2, 2, '1.08347'), 133761)],
 [('regression exponent-rescale 1', (806786, 2, 3, '113828.2'), 918349981652),
  ('regression exponent-rescale 2', (755323, 2, 4, '7.24667'), 547357652),
  ('control: precise rate', (123456, 2, 2, '1.08347'), 133761),
  ('control: large', (987654321, 2, 3, '0.30712'), 3033283951), ('control: cents to yen', (1000, 2, 0, '151.37'), 1514),
  ('control: float trap tie', (115, 2, 2, '1.1'), 126)],
 [('regression exponent-rescale 1', (943097, 3, 2, '109.9318'), 10367635),
  ('regression exponent-rescale 2', (286850, 2, 3, '1.495849'), 4290843),
  ('control: float trap tie', (115, 2, 2, '1.1'), 126), ('control: negative exact', (-250, 2, 2, '0.5'), -125),
  ('control: yen to dinar', (1, 0, 3, '0.3071'), 307), ('control: negative tie', (-3, 2, 2, '0.5'), -2)],
 [('regression exponent-rescale 1', (253357, 0, 4, '18.88901'), 47856629066),
  ('regression exponent-rescale 2', (190708, 0, 4, '188.1068'), 358734716144),
  ('control: negative tie', (-3, 2, 2, '0.5'), -2), ('control: zero', (0, 2, 0, '151.37'), 0),
  ('control: precise rate', (123456, 2, 2, '1.08347'), 133761),
  ('control: large', (987654321, 2, 3, '0.30712'), 3033283951)]]
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 exponent-rescale 11513701514Failed
regression exponent-rescale 20307Failed
control: float trap tie126126Passed
control: negative exact-125-125Passed
control: negative tie-2-2Passed
control: zero00Passed

SHA-256 / bbef126e8627570f69d8579e34945361a4e595704ba3fc2f5e8dece8d41f8c04

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, from_exp, to_exp, rate):
    scale = Decimal(10) ** (from_exp - to_exp)
    v = Decimal(amount_minor) * Decimal(rate) * scale
    return int(v.quantize(Decimal(1), rounding=ROUND_HALF_EVEN))
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
cases = [[('regression exponent-rescale 1', (1000, 2, 0, '151.37'), 1514),
  ('regression exponent-rescale 2', (1, 0, 3, '0.3071'), 307), ('control: float trap tie', (115, 2, 2, '1.1'), 126),
  ('control: negative exact', (-250, 2, 2, '0.5'), -125), ('control: negative tie', (-3, 2, 2, '0.5'), -2),
  ('control: zero', (0, 2, 0, '151.37'), 0)],
 [('regression exponent-rescale 1', (987654321, 2, 3, '0.30712'), 3033283951),
  ('regression exponent-rescale 2', (64824, 0, 2, '51.9345'), 336660203),
  ('control: yen to dinar', (1, 0, 3, '0.3071'), 307), ('control: negative tie', (-3, 2, 2, '0.5'), -2),
  ('control: zero', (0, 2, 0, '151.37'), 0), ('control: precise rate', (123456, 2, 2, '1.08347'), 133761)],
 [('regression exponent-rescale 1', (806786, 2, 3, '113828.2'), 918349981652),
  ('regression exponent-rescale 2', (755323, 2, 4, '7.24667'), 547357652),
  ('control: precise rate', (123456, 2, 2, '1.08347'), 133761),
  ('control: large', (987654321, 2, 3, '0.30712'), 3033283951), ('control: cents to yen', (1000, 2, 0, '151.37'), 1514),
  ('control: float trap tie', (115, 2, 2, '1.1'), 126)],
 [('regression exponent-rescale 1', (943097, 3, 2, '109.9318'), 10367635),
  ('regression exponent-rescale 2', (286850, 2, 3, '1.495849'), 4290843),
  ('control: float trap tie', (115, 2, 2, '1.1'), 126), ('control: negative exact', (-250, 2, 2, '0.5'), -125),
  ('control: yen to dinar', (1, 0, 3, '0.3071'), 307), ('control: negative tie', (-3, 2, 2, '0.5'), -2)],
 [('regression exponent-rescale 1', (253357, 0, 4, '18.88901'), 47856629066),
  ('regression exponent-rescale 2', (190708, 0, 4, '188.1068'), 358734716144),
  ('control: negative tie', (-3, 2, 2, '0.5'), -2), ('control: zero', (0, 2, 0, '151.37'), 0),
  ('control: precise rate', (123456, 2, 2, '1.08347'), 133761),
  ('control: large', (987654321, 2, 3, '0.30712'), 3033283951)]]
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 exponent-rescale 1151370001514Failed
regression exponent-rescale 20307Failed
control: float trap tie126126Passed
control: negative exact-125-125Passed
control: negative tie-2-2Passed
control: zero00Passed

SHA-256 / 2c80f6e91d653b2e7c2c775d7b5aeba8d76a4096d720bec22aa6ca7f55a6f71d

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, from_exp, to_exp, rate):
    scale = Decimal(10) ** (to_exp - from_exp)
    v = Decimal(amount_minor) * Decimal(rate) * scale
    return int(v.quantize(Decimal(1), rounding=ROUND_HALF_EVEN))
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
cases = [[('regression exponent-rescale 1', (1000, 2, 0, '151.37'), 1514),
  ('regression exponent-rescale 2', (1, 0, 3, '0.3071'), 307), ('control: float trap tie', (115, 2, 2, '1.1'), 126),
  ('control: negative exact', (-250, 2, 2, '0.5'), -125), ('control: negative tie', (-3, 2, 2, '0.5'), -2),
  ('control: zero', (0, 2, 0, '151.37'), 0)],
 [('regression exponent-rescale 1', (987654321, 2, 3, '0.30712'), 3033283951),
  ('regression exponent-rescale 2', (64824, 0, 2, '51.9345'), 336660203),
  ('control: yen to dinar', (1, 0, 3, '0.3071'), 307), ('control: negative tie', (-3, 2, 2, '0.5'), -2),
  ('control: zero', (0, 2, 0, '151.37'), 0), ('control: precise rate', (123456, 2, 2, '1.08347'), 133761)],
 [('regression exponent-rescale 1', (806786, 2, 3, '113828.2'), 918349981652),
  ('regression exponent-rescale 2', (755323, 2, 4, '7.24667'), 547357652),
  ('control: precise rate', (123456, 2, 2, '1.08347'), 133761),
  ('control: large', (987654321, 2, 3, '0.30712'), 3033283951), ('control: cents to yen', (1000, 2, 0, '151.37'), 1514),
  ('control: float trap tie', (115, 2, 2, '1.1'), 126)],
 [('regression exponent-rescale 1', (943097, 3, 2, '109.9318'), 10367635),
  ('regression exponent-rescale 2', (286850, 2, 3, '1.495849'), 4290843),
  ('control: float trap tie', (115, 2, 2, '1.1'), 126), ('control: negative exact', (-250, 2, 2, '0.5'), -125),
  ('control: yen to dinar', (1, 0, 3, '0.3071'), 307), ('control: negative tie', (-3, 2, 2, '0.5'), -2)],
 [('regression exponent-rescale 1', (253357, 0, 4, '18.88901'), 47856629066),
  ('regression exponent-rescale 2', (190708, 0, 4, '188.1068'), 358734716144),
  ('control: negative tie', (-3, 2, 2, '0.5'), -2), ('control: zero', (0, 2, 0, '151.37'), 0),
  ('control: precise rate', (123456, 2, 2, '1.08347'), 133761),
  ('control: large', (987654321, 2, 3, '0.30712'), 3033283951)]]
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 exponent-rescale 115141514Passed
regression exponent-rescale 2307307Passed
control: float trap tie126126Passed
control: negative exact-125-125Passed
control: negative tie-2-2Passed
control: zero00Passed

SHA-256 / b51562ec2ea32dcb39966a1d4f7ff61a99d7bd255083c16a187f7486917f2dcf

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

Case digest / 4f9d56da93264edda97bf58fd0ed843c665abe3872c86339561b7ecd5749a98f