FAILURE MAP
← Case archive

FA-321 / Floating-point arithmetic / Open access

Large cancelling terms erase a small summand · case 01

A mathematically nonzero residual disappears when large positive and negative terms cancel.

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

ROOT CAUSE

Sequential floating-point accumulation rounds away a small term before the compensating large term arrives.

VERIFIED REPAIR

Use an accurate summation routine that retains multiple partial sums across magnitude changes.

Unsuccessful approach: A single Kahan compensation term is still lost for some severe cancellation orderings; compensation is not a universal accuracy guarantee.

Case contract

Sum a finite sequence of finite binary floating-point values using math.fsum semantics. These bounded fixtures have exactly representable expected residuals and do not overflow the final result.

Why this case matters

Mixed-sign measurements with a large dynamic range can expose weaknesses that positive-only sums do not. The experiment compares ordinary accumulation, Kahan accumulation, and standard-library accurate summation.

1 / The failure

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(values):
    total = 0.0
    for value in values:
        total += value
    return total
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
large = float(2 ** 54)
residual = N + 0.125
check('small residual between cancelling large terms', solve([large, residual, -large]), residual)
check('negative residual between cancelling large terms', solve([-large, -residual, large]), -residual)
check('small terms before cancellation', solve([0.25] * N + [large, -large]), N / 4)
check('ordinary exact binary fractions', solve([0.125, 0.25, 0.5] * N), 0.875 * N)
check('opposite terms cancel exactly', solve([large, -large] * N), 0.0)
check('one finite observation', solve([residual]), residual)
check('empty sum', solve([]), 0.0)
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
small residual between cancelling large terms0.01.125Failed
negative residual between cancelling large terms0.0-1.125Failed
small terms before cancellation0.00.25Failed
ordinary exact binary fractions0.8750.875Passed
opposite terms cancel exactly0.00.0Passed
one finite observation1.1251.125Passed
empty sum0.00.0Passed

SHA-256 / 6dd777c539e82241d7c7d692a318b4b701bc9cbecc2057e2a9c96f8a52dfd558

2 / The unsuccessful fix

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(values):
    total, correction = 0.0, 0.0
    for value in values:
        adjusted = value - correction
        updated = total + adjusted
        correction = (updated - total) - adjusted
        total = updated
    return total
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
large = float(2 ** 54)
residual = N + 0.125
check('small residual between cancelling large terms', solve([large, residual, -large]), residual)
check('negative residual between cancelling large terms', solve([-large, -residual, large]), -residual)
check('small terms before cancellation', solve([0.25] * N + [large, -large]), N / 4)
check('ordinary exact binary fractions', solve([0.125, 0.25, 0.5] * N), 0.875 * N)
check('opposite terms cancel exactly', solve([large, -large] * N), 0.0)
check('one finite observation', solve([residual]), residual)
check('empty sum', solve([]), 0.0)
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
small residual between cancelling large terms2.01.125Failed
negative residual between cancelling large terms-2.0-1.125Failed
small terms before cancellation0.00.25Failed
ordinary exact binary fractions0.8750.875Passed
opposite terms cancel exactly0.00.0Passed
one finite observation1.1251.125Passed
empty sum0.00.0Passed

SHA-256 / 237bcce6055006b6423aca9fdce9c3cf4e0565f4538393cb06d0db09458db130

3 / The verified repair

Exit 0
"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(values):
    return math.fsum(values)
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
large = float(2 ** 54)
residual = N + 0.125
check('small residual between cancelling large terms', solve([large, residual, -large]), residual)
check('negative residual between cancelling large terms', solve([-large, -residual, large]), -residual)
check('small terms before cancellation', solve([0.25] * N + [large, -large]), N / 4)
check('ordinary exact binary fractions', solve([0.125, 0.25, 0.5] * N), 0.875 * N)
check('opposite terms cancel exactly', solve([large, -large] * N), 0.0)
check('one finite observation', solve([residual]), residual)
check('empty sum', solve([]), 0.0)
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
small residual between cancelling large terms1.1251.125Passed
negative residual between cancelling large terms-1.125-1.125Passed
small terms before cancellation0.250.25Passed
ordinary exact binary fractions0.8750.875Passed
opposite terms cancel exactly0.00.0Passed
one finite observation1.1251.125Passed
empty sum0.00.0Passed

SHA-256 / 324c2ff074f9def0f207d84c6897d4d1be72bcbea9565a7972037479efddbbe9

Verification & scope

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

Case digest / 4d4e34d217ad2be9bb4ba43caf527c3724b48c9ce168b549e5786068e9dff41e