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.
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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| regression exponent-rescale 1 | 151370 | 1514 | Failed |
| regression exponent-rescale 2 | 0 | 307 | Failed |
| control: float trap tie | 126 | 126 | Passed |
| control: negative exact | -125 | -125 | Passed |
| control: negative tie | -2 | -2 | Passed |
| control: zero | 0 | 0 | Passed |
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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| regression exponent-rescale 1 | 15137000 | 1514 | Failed |
| regression exponent-rescale 2 | 0 | 307 | Failed |
| control: float trap tie | 126 | 126 | Passed |
| control: negative exact | -125 | -125 | Passed |
| control: negative tie | -2 | -2 | Passed |
| control: zero | 0 | 0 | Passed |
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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| regression exponent-rescale 1 | 1514 | 1514 | Passed |
| regression exponent-rescale 2 | 307 | 307 | Passed |
| control: float trap tie | 126 | 126 | Passed |
| control: negative exact | -125 | -125 | Passed |
| control: negative tie | -2 | -2 | Passed |
| control: zero | 0 | 0 | Passed |
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