FAILURE MAP
← Case archive

FA-91476 / Digital signal filters / Open access

Q15 smoother truncates the product without a rounding offset · case 01

Negative steps settle one LSB short of the input; the filter has a persistent downward bias.

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

ROOT CAUSE

The 2**14 rounding offset is omitted before the arithmetic shift, so the update is floored.

VERIFIED REPAIR

Add 1 << 14 before shifting right by 15.

Unsuccessful approach: The attempted repair adds 1 << 15, a full LSB, which biases upward.

Case contract

Input [alpha, samples]; alpha is Q15 in [0, 32768] ("bad-alpha" otherwise). Inputs are saturated to int16; y += ((x - y) * alpha + 2**14) >> 15 (round half up on the Q15 product via arithmetic shift), starting at y = 0. Return the integer outputs.

Why this case matters

Fixed-point exponential smoothers run on microcontrollers; rounding direction and saturation slips cause bias and limit cycles.

1 / The failure

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

N = 1
observations = []
def solve(x):
    alpha, xs = x
    if not 0 <= alpha <= 32768:
        return 'bad-alpha'
    y = 0
    out = []
    for v in xs:
        v = max(-32768, min(32767, v))
        y = y + (((v - y) * alpha) >> 15)
        out.append(y)
    return out
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression: negative step', [16384, [-1, -1, -1]], [0, 0, 0]], ['regression: input beyond range', [16384, [40000, 40000, -40000]], [16384, 24576, -4096]], ['repair check: alpha one', [32768, [100, -5, 7]], [100, -5, 7]], ['control: bad alpha', [40000, [1]], 'bad-alpha'], ['regression: small negative tie', [16384, [-3, 0, 0]], [-1, 0, 0]], ['regression: random smoother 0', [24576, [-8, -38976, 37]], [-6, -24577, -6116]], ['regression: random smoother 1', [8192, [38, 13765]], [10, 3449]]], [['regression: random smoother 0', [24576, [-8, -38976, 37]], [-6, -24577, -6116]], ['regression: random smoother 1', [8192, [38, 13765]], [10, 3449]], ['regression: input beyond range', [16384, [40000, 40000, -40000]], [16384, 24576, -4096]], ['control: bad alpha', [40000, [1]], 'bad-alpha'], ['regression: small negative tie', [16384, [-3, 0, 0]], [-1, 0, 0]], ['regression: random smoother 6', [8192, [16294, 2, -779, 14009]], [4074, 3056, 2097, 5075]], ['regression: random smoother 7', [1000, [-8425, 39]], [-257, -248]]], [['regression: random smoother 7', [1000, [-8425, 39]], [-257, -248]], ['regression: random smoother 11', [24576, [12306, -35386, 1164, 8403, 12704, 26]], [9230, -22268, -4694, 5129, 10810, 2722]], ['regression: random smoother 0', [24576, [-8, -38976, 37]], [-6, -24577, -6116]], ['control: bad alpha', [40000, [1]], 'bad-alpha'], ['regression: random smoother 1', [8192, [38, 13765]], [10, 3449]], ['regression: random smoother 6', [8192, [16294, 2, -779, 14009]], [4074, 3056, 2097, 5075]], ['regression: random smoother 12', [16384, [-15862, 2530, 35915]], [-7931, -2700, 15034]]], [['regression: random smoother 14', [24576, [-12991, -22604, 32, -18621, -30, -36, -10331]], [-9743, -19389, -4823, -15171, -3815, -981, -7993]], ['regression: random smoother 15', [30000, [13636, -12457, -1, -11, 49, 0]], [12484, -10350, -875, -84, 38, 3]], ['repair check: random smoother 3', [32768, [8, 9547, 10, -20745, -49, 4]], [8, 9547, 10, -20745, -49, 4]], ['control: bad alpha', [40000, [1]], 'bad-alpha'], ['regression: random smoother 1', [8192, [38, 13765]], [10, 3449]], ['regression: random smoother 6', [8192, [16294, 2, -779, 14009]], [4074, 3056, 2097, 5075]], ['regression: random smoother 7', [1000, [-8425, 39]], [-257, -248]]], [['regression: random smoother 17', [16384, [-8485, -763, -9, 25855, -11982, -28816, 38]], [-4242, -2502, -1255, 12300, 159, -14328, -7145]], ['regression: random smoother 18', [24576, [-8497, -36]], [-6373, -1620]], ['repair check: random smoother 5', [32768, [5, 7014, 39535]], [5, 7014, 32767]], ['control: bad alpha', [40000, [1]], 'bad-alpha'], ['regression: random smoother 6', [8192, [16294, 2, -779, 14009]], [4074, 3056, 2097, 5075]], ['regression: random smoother 7', [1000, [-8425, 39]], [-257, -248]], ['regression: random smoother 11', [24576, [12306, -35386, 1164, 8403, 12704, 26]], [9230, -22268, -4694, 5129, 10810, 2722]]]]
for label, args, expected in fixtures[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: negative step[-1, -1, -1][0, 0, 0]Failed
regression: input beyond range[16383, 24575, -4097][16384, 24576, -4096]Failed
repair check: alpha one[100, -5, 7][100, -5, 7]Passed
control: bad alphabad-alphabad-alphaPassed
regression: small negative tie[-2, -1, -1][-1, 0, 0]Failed
regression: random smoother 0[-6, -24578, -6117][-6, -24577, -6116]Failed
regression: random smoother 1[9, 3448][10, 3449]Failed

SHA-256 / a409fa4a5ce280a9fe8b83a80b99665838bf0ab7689d0e6d373c85ebadad64aa

2 / The unsuccessful fix

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

N = 1
observations = []
def solve(x):
    alpha, xs = x
    if not 0 <= alpha <= 32768:
        return 'bad-alpha'
    y = 0
    out = []
    for v in xs:
        v = max(-32768, min(32767, v))
        y = y + (((v - y) * alpha + (1 << 15)) >> 15)
        out.append(y)
    return out
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression: negative step', [16384, [-1, -1, -1]], [0, 0, 0]], ['regression: input beyond range', [16384, [40000, 40000, -40000]], [16384, 24576, -4096]], ['repair check: alpha one', [32768, [100, -5, 7]], [100, -5, 7]], ['control: bad alpha', [40000, [1]], 'bad-alpha'], ['regression: small negative tie', [16384, [-3, 0, 0]], [-1, 0, 0]], ['regression: random smoother 0', [24576, [-8, -38976, 37]], [-6, -24577, -6116]], ['regression: random smoother 1', [8192, [38, 13765]], [10, 3449]]], [['regression: random smoother 0', [24576, [-8, -38976, 37]], [-6, -24577, -6116]], ['regression: random smoother 1', [8192, [38, 13765]], [10, 3449]], ['regression: input beyond range', [16384, [40000, 40000, -40000]], [16384, 24576, -4096]], ['control: bad alpha', [40000, [1]], 'bad-alpha'], ['regression: small negative tie', [16384, [-3, 0, 0]], [-1, 0, 0]], ['regression: random smoother 6', [8192, [16294, 2, -779, 14009]], [4074, 3056, 2097, 5075]], ['regression: random smoother 7', [1000, [-8425, 39]], [-257, -248]]], [['regression: random smoother 7', [1000, [-8425, 39]], [-257, -248]], ['regression: random smoother 11', [24576, [12306, -35386, 1164, 8403, 12704, 26]], [9230, -22268, -4694, 5129, 10810, 2722]], ['regression: random smoother 0', [24576, [-8, -38976, 37]], [-6, -24577, -6116]], ['control: bad alpha', [40000, [1]], 'bad-alpha'], ['regression: random smoother 1', [8192, [38, 13765]], [10, 3449]], ['regression: random smoother 6', [8192, [16294, 2, -779, 14009]], [4074, 3056, 2097, 5075]], ['regression: random smoother 12', [16384, [-15862, 2530, 35915]], [-7931, -2700, 15034]]], [['regression: random smoother 14', [24576, [-12991, -22604, 32, -18621, -30, -36, -10331]], [-9743, -19389, -4823, -15171, -3815, -981, -7993]], ['regression: random smoother 15', [30000, [13636, -12457, -1, -11, 49, 0]], [12484, -10350, -875, -84, 38, 3]], ['repair check: random smoother 3', [32768, [8, 9547, 10, -20745, -49, 4]], [8, 9547, 10, -20745, -49, 4]], ['control: bad alpha', [40000, [1]], 'bad-alpha'], ['regression: random smoother 1', [8192, [38, 13765]], [10, 3449]], ['regression: random smoother 6', [8192, [16294, 2, -779, 14009]], [4074, 3056, 2097, 5075]], ['regression: random smoother 7', [1000, [-8425, 39]], [-257, -248]]], [['regression: random smoother 17', [16384, [-8485, -763, -9, 25855, -11982, -28816, 38]], [-4242, -2502, -1255, 12300, 159, -14328, -7145]], ['regression: random smoother 18', [24576, [-8497, -36]], [-6373, -1620]], ['repair check: random smoother 5', [32768, [5, 7014, 39535]], [5, 7014, 32767]], ['control: bad alpha', [40000, [1]], 'bad-alpha'], ['regression: random smoother 6', [8192, [16294, 2, -779, 14009]], [4074, 3056, 2097, 5075]], ['regression: random smoother 7', [1000, [-8425, 39]], [-257, -248]], ['regression: random smoother 11', [24576, [12306, -35386, 1164, 8403, 12704, 26]], [9230, -22268, -4694, 5129, 10810, 2722]]]]
for label, args, expected in fixtures[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: negative step[0, 0, 0][0, 0, 0]Passed
regression: input beyond range[16384, 24576, -4095][16384, 24576, -4096]Failed
repair check: alpha one[101, -4, 8][100, -5, 7]Failed
control: bad alphabad-alphabad-alphaPassed
regression: small negative tie[-1, 0, 1][-1, 0, 0]Failed
regression: random smoother 0[-5, -24577, -6116][-6, -24577, -6116]Failed
regression: random smoother 1[10, 3449][10, 3449]Passed

SHA-256 / 3b1c5ba5faa28edf7d4c472221d1610a6b4cdb9ef4268835e7f81e02d19cb7af

3 / The verified repair

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

N = 1
observations = []
def solve(x):
    alpha, xs = x
    if not 0 <= alpha <= 32768:
        return 'bad-alpha'
    y = 0
    out = []
    for v in xs:
        v = max(-32768, min(32767, v))
        y = y + (((v - y) * alpha + (1 << 14)) >> 15)
        out.append(y)
    return out
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression: negative step', [16384, [-1, -1, -1]], [0, 0, 0]], ['regression: input beyond range', [16384, [40000, 40000, -40000]], [16384, 24576, -4096]], ['repair check: alpha one', [32768, [100, -5, 7]], [100, -5, 7]], ['control: bad alpha', [40000, [1]], 'bad-alpha'], ['regression: small negative tie', [16384, [-3, 0, 0]], [-1, 0, 0]], ['regression: random smoother 0', [24576, [-8, -38976, 37]], [-6, -24577, -6116]], ['regression: random smoother 1', [8192, [38, 13765]], [10, 3449]]], [['regression: random smoother 0', [24576, [-8, -38976, 37]], [-6, -24577, -6116]], ['regression: random smoother 1', [8192, [38, 13765]], [10, 3449]], ['regression: input beyond range', [16384, [40000, 40000, -40000]], [16384, 24576, -4096]], ['control: bad alpha', [40000, [1]], 'bad-alpha'], ['regression: small negative tie', [16384, [-3, 0, 0]], [-1, 0, 0]], ['regression: random smoother 6', [8192, [16294, 2, -779, 14009]], [4074, 3056, 2097, 5075]], ['regression: random smoother 7', [1000, [-8425, 39]], [-257, -248]]], [['regression: random smoother 7', [1000, [-8425, 39]], [-257, -248]], ['regression: random smoother 11', [24576, [12306, -35386, 1164, 8403, 12704, 26]], [9230, -22268, -4694, 5129, 10810, 2722]], ['regression: random smoother 0', [24576, [-8, -38976, 37]], [-6, -24577, -6116]], ['control: bad alpha', [40000, [1]], 'bad-alpha'], ['regression: random smoother 1', [8192, [38, 13765]], [10, 3449]], ['regression: random smoother 6', [8192, [16294, 2, -779, 14009]], [4074, 3056, 2097, 5075]], ['regression: random smoother 12', [16384, [-15862, 2530, 35915]], [-7931, -2700, 15034]]], [['regression: random smoother 14', [24576, [-12991, -22604, 32, -18621, -30, -36, -10331]], [-9743, -19389, -4823, -15171, -3815, -981, -7993]], ['regression: random smoother 15', [30000, [13636, -12457, -1, -11, 49, 0]], [12484, -10350, -875, -84, 38, 3]], ['repair check: random smoother 3', [32768, [8, 9547, 10, -20745, -49, 4]], [8, 9547, 10, -20745, -49, 4]], ['control: bad alpha', [40000, [1]], 'bad-alpha'], ['regression: random smoother 1', [8192, [38, 13765]], [10, 3449]], ['regression: random smoother 6', [8192, [16294, 2, -779, 14009]], [4074, 3056, 2097, 5075]], ['regression: random smoother 7', [1000, [-8425, 39]], [-257, -248]]], [['regression: random smoother 17', [16384, [-8485, -763, -9, 25855, -11982, -28816, 38]], [-4242, -2502, -1255, 12300, 159, -14328, -7145]], ['regression: random smoother 18', [24576, [-8497, -36]], [-6373, -1620]], ['repair check: random smoother 5', [32768, [5, 7014, 39535]], [5, 7014, 32767]], ['control: bad alpha', [40000, [1]], 'bad-alpha'], ['regression: random smoother 6', [8192, [16294, 2, -779, 14009]], [4074, 3056, 2097, 5075]], ['regression: random smoother 7', [1000, [-8425, 39]], [-257, -248]], ['regression: random smoother 11', [24576, [12306, -35386, 1164, 8403, 12704, 26]], [9230, -22268, -4694, 5129, 10810, 2722]]]]
for label, args, expected in fixtures[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: negative step[0, 0, 0][0, 0, 0]Passed
regression: input beyond range[16384, 24576, -4096][16384, 24576, -4096]Passed
repair check: alpha one[100, -5, 7][100, -5, 7]Passed
control: bad alphabad-alphabad-alphaPassed
regression: small negative tie[-1, 0, 0][-1, 0, 0]Passed
regression: random smoother 0[-6, -24577, -6116][-6, -24577, -6116]Passed
regression: random smoother 1[10, 3449][10, 3449]Passed

SHA-256 / d6a8112afeda2f5acc6ec0038587c834283b3eae534b5958c3b2a475bcbe207f

Verification & scope

A deterministic bounded teaching model with a stipulated toy contract; exact rational arithmetic or fixed-decimal rounding keeps outputs strict JSON. It is not a production DSP library and claims no standards conformance. 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:51:36.306076+00:00.

Case digest / d008a7d2114c90df1bac7ae00175154019f02ae15c49b4ee632710250a84ff1e