FA-84381 / Betting odds conversion / Open access
Decimal price rounded with binary floats or truncated · case 01
A -800 line shows 1.12 instead of 1.13.
ROOT CAUSE
The decimal price is formatted with '%.2f' on a float, rounding the binary value.
VERIFIED REPAIR
Round the exact value half up.
Unsuccessful approach: Truncating inside the helper also drops the half-cent.
Case contract
Expand an American moneyline ("+150", "150" or "-200"; magnitude at least 100, otherwise "invalid"). Profit per unit is N/100 for positive or unsigned lines and 100/N for negative lines. Return [decimal price with two decimals, reduced fractional profit "a/b", implied probability percent with one decimal], all computed exactly and rounded half up.
Why this case matters
Odds screens show American, decimal, fractional and implied-probability columns for one price.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
from fractions import Fraction
import math
N = 1
observations = []
def solve(american):
s = american.strip()
neg = s.startswith('-')
n = int(s.lstrip('+-'))
if n < 100:
return 'invalid'
profit = Fraction(100, n) if neg else Fraction(n, 100)
dec = 1 + profit
def fmt(x, places):
q = 10 ** places
v = math.floor(x * q + Fraction(1, 2))
return '%d.%0*d' % (v // q, places, v % q)
prob = Fraction(n, n + 100) * 100 if neg else Fraction(100, n + 100) * 100
return ['%.2f' % float(dec), '%d/%d' % (profit.numerator, profit.denominator), fmt(prob, 1)]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
def run(args):
try:
return solve(*args)
except Exception as exc:
return 'raised ' + type(exc).__name__
cases = [[('control underdog', ('+150',), ['2.50', '3/2', '40.0']),
('control favourite', ('-200',), ['1.50', '1/2', '66.7']),
('boundary even money', ('+100',), ['2.00', '1/1', '50.0']),
('boundary unsigned line', ('250',), ['3.50', '5/2', '28.6']),
('boundary below minimum', ('+50',), 'invalid'),
('control half-up decimal', ('-800',), ['1.13', '1/8', '88.9']),
('regression: decimal rounding', ('-331',), ['1.30', '100/331', '76.8']),
('regression: decimal rounding', ('-800',), ['1.13', '1/8', '88.9']),
('variant scenario 1', ('-100',), ['2.00', '1/1', '50.0']),
('variant scenario 2', ('+208',), ['3.08', '52/25', '32.5'])],
[('control underdog', ('+150',), ['2.50', '3/2', '40.0']),
('control favourite', ('-200',), ['1.50', '1/2', '66.7']),
('boundary even money', ('+100',), ['2.00', '1/1', '50.0']),
('boundary unsigned line', ('250',), ['3.50', '5/2', '28.6']),
('boundary below minimum', ('+50',), 'invalid'),
('control half-up decimal', ('-800',), ['1.13', '1/8', '88.9']),
('regression: decimal rounding', ('895',), ['9.95', '179/20', '10.1']),
('regression: decimal rounding', ('-800',), ['1.13', '1/8', '88.9']),
('variant scenario 1', ('-120',), ['1.83', '5/6', '54.5']),
('variant scenario 2', ('+210',), ['3.10', '21/10', '32.3'])],
[('control underdog', ('+150',), ['2.50', '3/2', '40.0']),
('control favourite', ('-200',), ['1.50', '1/2', '66.7']),
('boundary even money', ('+100',), ['2.00', '1/1', '50.0']),
('boundary unsigned line', ('250',), ['3.50', '5/2', '28.6']),
('boundary below minimum', ('+50',), 'invalid'),
('control half-up decimal', ('-800',), ['1.13', '1/8', '88.9']),
('regression: decimal rounding', ('-1004',), ['1.10', '25/251', '90.9']),
('regression: decimal rounding', ('-800',), ['1.13', '1/8', '88.9']),
('variant scenario 1', ('+168',), ['2.68', '42/25', '37.3']),
('variant scenario 2', ('217',), ['3.17', '217/100', '31.5'])],
[('control underdog', ('+150',), ['2.50', '3/2', '40.0']),
('control favourite', ('-200',), ['1.50', '1/2', '66.7']),
('boundary even money', ('+100',), ['2.00', '1/1', '50.0']),
('boundary unsigned line', ('250',), ['3.50', '5/2', '28.6']),
('boundary below minimum', ('+50',), 'invalid'),
('control half-up decimal', ('-800',), ['1.13', '1/8', '88.9']),
('regression: decimal rounding', ('-674',), ['1.15', '50/337', '87.1']),
('regression: decimal rounding', ('-800',), ['1.13', '1/8', '88.9']),
('variant scenario 1', ('+50',), 'invalid'),
('variant scenario 2', ('149',), ['2.49', '149/100', '40.2'])],
[('control underdog', ('+150',), ['2.50', '3/2', '40.0']),
('control favourite', ('-200',), ['1.50', '1/2', '66.7']),
('boundary even money', ('+100',), ['2.00', '1/1', '50.0']),
('boundary unsigned line', ('250',), ['3.50', '5/2', '28.6']),
('boundary below minimum', ('+50',), 'invalid'),
('control half-up decimal', ('-800',), ['1.13', '1/8', '88.9']),
('regression: decimal rounding', ('-624',), ['1.16', '25/156', '86.2']),
('regression: decimal rounding', ('-800',), ['1.13', '1/8', '88.9']),
('variant scenario 1', ('+125',), ['2.25', '5/4', '44.4']),
('variant scenario 2', ('+119',), ['2.19', '119/100', '45.7'])]]
for label, args, expected in cases[N - 1]:
check(label, run(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 |
|---|---|---|---|
| control underdog | ['2.50', '3/2', '40.0'] | ['2.50', '3/2', '40.0'] | Passed |
| control favourite | ['1.50', '1/2', '66.7'] | ['1.50', '1/2', '66.7'] | Passed |
| boundary even money | ['2.00', '1/1', '50.0'] | ['2.00', '1/1', '50.0'] | Passed |
| boundary unsigned line | ['3.50', '5/2', '28.6'] | ['3.50', '5/2', '28.6'] | Passed |
| boundary below minimum | invalid | invalid | Passed |
| control half-up decimal | ['1.12', '1/8', '88.9'] | ['1.13', '1/8', '88.9'] | Failed |
| regression: decimal rounding | ['1.30', '100/331', '76.8'] | ['1.30', '100/331', '76.8'] | Passed |
| regression: decimal rounding | ['1.12', '1/8', '88.9'] | ['1.13', '1/8', '88.9'] | Failed |
| variant scenario 1 | ['2.00', '1/1', '50.0'] | ['2.00', '1/1', '50.0'] | Passed |
| variant scenario 2 | ['3.08', '52/25', '32.5'] | ['3.08', '52/25', '32.5'] | Passed |
SHA-256 / 7b0ca169891b24758852e34751cd0f82ef72e8b1edb915075190bbbac9932e57
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
from fractions import Fraction
import math
N = 1
observations = []
def solve(american):
s = american.strip()
neg = s.startswith('-')
n = int(s.lstrip('+-'))
if n < 100:
return 'invalid'
profit = Fraction(100, n) if neg else Fraction(n, 100)
dec = 1 + profit
def fmt(x, places):
q = 10 ** places
v = math.floor(x * q)
return '%d.%0*d' % (v // q, places, v % q)
prob = Fraction(n, n + 100) * 100 if neg else Fraction(100, n + 100) * 100
return [fmt(dec, 2), '%d/%d' % (profit.numerator, profit.denominator), fmt(prob, 1)]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
def run(args):
try:
return solve(*args)
except Exception as exc:
return 'raised ' + type(exc).__name__
cases = [[('control underdog', ('+150',), ['2.50', '3/2', '40.0']),
('control favourite', ('-200',), ['1.50', '1/2', '66.7']),
('boundary even money', ('+100',), ['2.00', '1/1', '50.0']),
('boundary unsigned line', ('250',), ['3.50', '5/2', '28.6']),
('boundary below minimum', ('+50',), 'invalid'),
('control half-up decimal', ('-800',), ['1.13', '1/8', '88.9']),
('regression: decimal rounding', ('-331',), ['1.30', '100/331', '76.8']),
('regression: decimal rounding', ('-800',), ['1.13', '1/8', '88.9']),
('variant scenario 1', ('-100',), ['2.00', '1/1', '50.0']),
('variant scenario 2', ('+208',), ['3.08', '52/25', '32.5'])],
[('control underdog', ('+150',), ['2.50', '3/2', '40.0']),
('control favourite', ('-200',), ['1.50', '1/2', '66.7']),
('boundary even money', ('+100',), ['2.00', '1/1', '50.0']),
('boundary unsigned line', ('250',), ['3.50', '5/2', '28.6']),
('boundary below minimum', ('+50',), 'invalid'),
('control half-up decimal', ('-800',), ['1.13', '1/8', '88.9']),
('regression: decimal rounding', ('895',), ['9.95', '179/20', '10.1']),
('regression: decimal rounding', ('-800',), ['1.13', '1/8', '88.9']),
('variant scenario 1', ('-120',), ['1.83', '5/6', '54.5']),
('variant scenario 2', ('+210',), ['3.10', '21/10', '32.3'])],
[('control underdog', ('+150',), ['2.50', '3/2', '40.0']),
('control favourite', ('-200',), ['1.50', '1/2', '66.7']),
('boundary even money', ('+100',), ['2.00', '1/1', '50.0']),
('boundary unsigned line', ('250',), ['3.50', '5/2', '28.6']),
('boundary below minimum', ('+50',), 'invalid'),
('control half-up decimal', ('-800',), ['1.13', '1/8', '88.9']),
('regression: decimal rounding', ('-1004',), ['1.10', '25/251', '90.9']),
('regression: decimal rounding', ('-800',), ['1.13', '1/8', '88.9']),
('variant scenario 1', ('+168',), ['2.68', '42/25', '37.3']),
('variant scenario 2', ('217',), ['3.17', '217/100', '31.5'])],
[('control underdog', ('+150',), ['2.50', '3/2', '40.0']),
('control favourite', ('-200',), ['1.50', '1/2', '66.7']),
('boundary even money', ('+100',), ['2.00', '1/1', '50.0']),
('boundary unsigned line', ('250',), ['3.50', '5/2', '28.6']),
('boundary below minimum', ('+50',), 'invalid'),
('control half-up decimal', ('-800',), ['1.13', '1/8', '88.9']),
('regression: decimal rounding', ('-674',), ['1.15', '50/337', '87.1']),
('regression: decimal rounding', ('-800',), ['1.13', '1/8', '88.9']),
('variant scenario 1', ('+50',), 'invalid'),
('variant scenario 2', ('149',), ['2.49', '149/100', '40.2'])],
[('control underdog', ('+150',), ['2.50', '3/2', '40.0']),
('control favourite', ('-200',), ['1.50', '1/2', '66.7']),
('boundary even money', ('+100',), ['2.00', '1/1', '50.0']),
('boundary unsigned line', ('250',), ['3.50', '5/2', '28.6']),
('boundary below minimum', ('+50',), 'invalid'),
('control half-up decimal', ('-800',), ['1.13', '1/8', '88.9']),
('regression: decimal rounding', ('-624',), ['1.16', '25/156', '86.2']),
('regression: decimal rounding', ('-800',), ['1.13', '1/8', '88.9']),
('variant scenario 1', ('+125',), ['2.25', '5/4', '44.4']),
('variant scenario 2', ('+119',), ['2.19', '119/100', '45.7'])]]
for label, args, expected in cases[N - 1]:
check(label, run(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 |
|---|---|---|---|
| control underdog | ['2.50', '3/2', '40.0'] | ['2.50', '3/2', '40.0'] | Passed |
| control favourite | ['1.50', '1/2', '66.6'] | ['1.50', '1/2', '66.7'] | Failed |
| boundary even money | ['2.00', '1/1', '50.0'] | ['2.00', '1/1', '50.0'] | Passed |
| boundary unsigned line | ['3.50', '5/2', '28.5'] | ['3.50', '5/2', '28.6'] | Failed |
| boundary below minimum | invalid | invalid | Passed |
| control half-up decimal | ['1.12', '1/8', '88.8'] | ['1.13', '1/8', '88.9'] | Failed |
| regression: decimal rounding | ['1.30', '100/331', '76.7'] | ['1.30', '100/331', '76.8'] | Failed |
| regression: decimal rounding | ['1.12', '1/8', '88.8'] | ['1.13', '1/8', '88.9'] | Failed |
| variant scenario 1 | ['2.00', '1/1', '50.0'] | ['2.00', '1/1', '50.0'] | Passed |
| variant scenario 2 | ['3.08', '52/25', '32.4'] | ['3.08', '52/25', '32.5'] | Failed |
SHA-256 / cb3d673a76b3f8f0643c0f87e8fad94700425cc51c7dfb658bcc741df98a7342
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
from fractions import Fraction
import math
N = 1
observations = []
def solve(american):
s = american.strip()
neg = s.startswith('-')
n = int(s.lstrip('+-'))
if n < 100:
return 'invalid'
profit = Fraction(100, n) if neg else Fraction(n, 100)
dec = 1 + profit
def fmt(x, places):
q = 10 ** places
v = math.floor(x * q + Fraction(1, 2))
return '%d.%0*d' % (v // q, places, v % q)
prob = Fraction(n, n + 100) * 100 if neg else Fraction(100, n + 100) * 100
return [fmt(dec, 2), '%d/%d' % (profit.numerator, profit.denominator), fmt(prob, 1)]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
def run(args):
try:
return solve(*args)
except Exception as exc:
return 'raised ' + type(exc).__name__
cases = [[('control underdog', ('+150',), ['2.50', '3/2', '40.0']),
('control favourite', ('-200',), ['1.50', '1/2', '66.7']),
('boundary even money', ('+100',), ['2.00', '1/1', '50.0']),
('boundary unsigned line', ('250',), ['3.50', '5/2', '28.6']),
('boundary below minimum', ('+50',), 'invalid'),
('control half-up decimal', ('-800',), ['1.13', '1/8', '88.9']),
('regression: decimal rounding', ('-331',), ['1.30', '100/331', '76.8']),
('regression: decimal rounding', ('-800',), ['1.13', '1/8', '88.9']),
('variant scenario 1', ('-100',), ['2.00', '1/1', '50.0']),
('variant scenario 2', ('+208',), ['3.08', '52/25', '32.5'])],
[('control underdog', ('+150',), ['2.50', '3/2', '40.0']),
('control favourite', ('-200',), ['1.50', '1/2', '66.7']),
('boundary even money', ('+100',), ['2.00', '1/1', '50.0']),
('boundary unsigned line', ('250',), ['3.50', '5/2', '28.6']),
('boundary below minimum', ('+50',), 'invalid'),
('control half-up decimal', ('-800',), ['1.13', '1/8', '88.9']),
('regression: decimal rounding', ('895',), ['9.95', '179/20', '10.1']),
('regression: decimal rounding', ('-800',), ['1.13', '1/8', '88.9']),
('variant scenario 1', ('-120',), ['1.83', '5/6', '54.5']),
('variant scenario 2', ('+210',), ['3.10', '21/10', '32.3'])],
[('control underdog', ('+150',), ['2.50', '3/2', '40.0']),
('control favourite', ('-200',), ['1.50', '1/2', '66.7']),
('boundary even money', ('+100',), ['2.00', '1/1', '50.0']),
('boundary unsigned line', ('250',), ['3.50', '5/2', '28.6']),
('boundary below minimum', ('+50',), 'invalid'),
('control half-up decimal', ('-800',), ['1.13', '1/8', '88.9']),
('regression: decimal rounding', ('-1004',), ['1.10', '25/251', '90.9']),
('regression: decimal rounding', ('-800',), ['1.13', '1/8', '88.9']),
('variant scenario 1', ('+168',), ['2.68', '42/25', '37.3']),
('variant scenario 2', ('217',), ['3.17', '217/100', '31.5'])],
[('control underdog', ('+150',), ['2.50', '3/2', '40.0']),
('control favourite', ('-200',), ['1.50', '1/2', '66.7']),
('boundary even money', ('+100',), ['2.00', '1/1', '50.0']),
('boundary unsigned line', ('250',), ['3.50', '5/2', '28.6']),
('boundary below minimum', ('+50',), 'invalid'),
('control half-up decimal', ('-800',), ['1.13', '1/8', '88.9']),
('regression: decimal rounding', ('-674',), ['1.15', '50/337', '87.1']),
('regression: decimal rounding', ('-800',), ['1.13', '1/8', '88.9']),
('variant scenario 1', ('+50',), 'invalid'),
('variant scenario 2', ('149',), ['2.49', '149/100', '40.2'])],
[('control underdog', ('+150',), ['2.50', '3/2', '40.0']),
('control favourite', ('-200',), ['1.50', '1/2', '66.7']),
('boundary even money', ('+100',), ['2.00', '1/1', '50.0']),
('boundary unsigned line', ('250',), ['3.50', '5/2', '28.6']),
('boundary below minimum', ('+50',), 'invalid'),
('control half-up decimal', ('-800',), ['1.13', '1/8', '88.9']),
('regression: decimal rounding', ('-624',), ['1.16', '25/156', '86.2']),
('regression: decimal rounding', ('-800',), ['1.13', '1/8', '88.9']),
('variant scenario 1', ('+125',), ['2.25', '5/4', '44.4']),
('variant scenario 2', ('+119',), ['2.19', '119/100', '45.7'])]]
for label, args, expected in cases[N - 1]:
check(label, run(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 |
|---|---|---|---|
| control underdog | ['2.50', '3/2', '40.0'] | ['2.50', '3/2', '40.0'] | Passed |
| control favourite | ['1.50', '1/2', '66.7'] | ['1.50', '1/2', '66.7'] | Passed |
| boundary even money | ['2.00', '1/1', '50.0'] | ['2.00', '1/1', '50.0'] | Passed |
| boundary unsigned line | ['3.50', '5/2', '28.6'] | ['3.50', '5/2', '28.6'] | Passed |
| boundary below minimum | invalid | invalid | Passed |
| control half-up decimal | ['1.13', '1/8', '88.9'] | ['1.13', '1/8', '88.9'] | Passed |
| regression: decimal rounding | ['1.30', '100/331', '76.8'] | ['1.30', '100/331', '76.8'] | Passed |
| regression: decimal rounding | ['1.13', '1/8', '88.9'] | ['1.13', '1/8', '88.9'] | Passed |
| variant scenario 1 | ['2.00', '1/1', '50.0'] | ['2.00', '1/1', '50.0'] | Passed |
| variant scenario 2 | ['3.08', '52/25', '32.5'] | ['3.08', '52/25', '32.5'] | Passed |
SHA-256 / f44d7aba9efca5b192b7f57c2699a3ff155dac84ccf00047b3d8f8e1fc41240c
Verification & scope
Stipulated, bounded toy contract stated in the contract field; not a claim of conformance with any operator, exchange or regulator rule set. 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:50:30.421963+00:00.
Case digest / 6925455b8237421a1f5cd9e9d6cd747974a00bcdd9a8a207a7784341d23bc00f