Typorithm · easy

Numerical assertions with honest tolerances

pythonpytesttestingindependent-practice
Saved on this device
Full lesson · 4 learning steps Open any exercise for focused practice
1

Step 1 of 4 · intro card

Numerical assertions with honest tolerances

0%

Concept and contract

Choose absolute and relative error limits from the quantity being tested.

Binary floating-point cannot represent every decimal fraction exactly. A calculation such as 0.1 + 0.2 can differ slightly from the literal 0.3 even when the algorithm is correct. This is a representation issue, not permission to accept arbitrary numerical error.

Choose a tolerance from the quantity and requirement. Absolute tolerance limits an error in the same units as the result; relative tolerance scales with the expected magnitude. For values near zero, a relative tolerance alone provides little useful room. Explain the units and accepted accuracy before choosing numbers.

Mechanism and worked example

pytest.approx compares values using explicit relative and absolute tolerances.

pytest.approx compares values using explicit relative and absolute tolerances. The complete example totals a small collection, then compares the result with an independently calculated decimal target. It keeps the tolerance tight enough to reject a meaningful added charge.

When rel and abs are both supplied, meeting either tolerance can be sufficient. This is not a requirement to satisfy both simultaneously. The example explicitly supplies both values so readers can inspect the intended bound rather than assuming that library defaults match their domain.

Complete worked example

python

import pytest


def total(values):
    return sum(values)


def test_decimal_total():
    assert total([0.1, 0.2]) == pytest.approx(0.3, rel=1e-9, abs=1e-12)
Reasoning
pytest.approx compares values using explicit relative and absolute tolerances. The complete example totals a small collection, then compares the result with an independently calculated decimal target. It keeps the tolerance tight enough to reject a meaningful added charge. When rel and abs are both supplied, meeting either tolerance can be sufficient. This is not a requirement to satisfy both simultaneously. The example explicitly supplies both values so readers can inspect the intended bound rather than assuming that library defaults match their domain.
Open in local Python playground ↗

Focused test decision: context is in the complete example

python

assert total([0.1, 0.2]) == pytest.approx(0.3, rel=1e-9, abs=1e-12)
Focused test decision
Type this short test decision after understanding the complete runnable module. Desktop typing records practice; Run tests in the local playground executes real pytest.

Counterexamples and common mistakes

The controlled bug adds 0.01 to every total.

The controlled bug adds 0.01 to every total. A tolerance of 0.1 might conceal it; the stated small bounds reject it. Rounding both results before comparison can also erase a real failure without documenting a permissible error in the original units.

NaN does not normally compare equal under approx unless nan_ok is requested. Infinity has special equality behavior. Do not add nan_ok merely to make an unexpected missing result pass: first decide whether non-finite output is valid for the calculation.

Controlled bug: the tests must report failure

python

import pytest


def total(values):
    return sum(values) + 0.01


def test_decimal_total():
    assert total([0.1, 0.2]) == pytest.approx(0.3, rel=1e-9, abs=1e-12)
Reasoning
The controlled bug adds 0.01 to every total. A tolerance of 0.1 might conceal it; the stated small bounds reject it. Rounding both results before comparison can also erase a real failure without documenting a permissible error in the original units. NaN does not normally compare equal under approx unless nan_ok is requested. Infinity has special equality behavior. Do not add nan_ok merely to make an unexpected missing result pass: first decide whether non-finite output is valid for the calculation.
Open in local Python playground ↗

Independent practice

Run the decimal-sum case and explain why exact equality is unsuitable for that result.

Run the decimal-sum case and explain why exact equality is unsuitable for that result. Add a zero-total test for an empty input, using the same explicit tolerances. Predict which bound matters near zero.

Keep the exercise focused on the assertion. Do not replace financial accounting with binary floats just because approx exists; decimal money or integer minor units can be a more appropriate representation. The toy totals here teach numerical comparison, not a payments implementation.

Step 1: Add a zero-total test with explicit absolute and relative tolerance. Step 2: Keep the existing passing cases. Add the smallest new assertion first; predict the failure before running pytest.

Practice starter: solve before reading the solution

python

import pytest


def total(values):
    return sum(values)


def test_decimal_total():
    assert total([0.1, 0.2]) == pytest.approx(0.3, rel=1e-9, abs=1e-12)

# Task: Add a zero-total test with explicit absolute and relative tolerance.
Reasoning
Add a zero-total test with explicit absolute and relative tolerance.
Open in local Python playground ↗
  • Hint 1: An empty input has a mathematically known total.
  • Hint 2: Near zero the absolute tolerance is the useful bound.

Solution, tests and trade-offs

The solution adds the empty total.

The solution adds the empty total. Its probe changes the function to add a tiny 1e-6 only for empty input. The first nonempty example remains correct, while the new zero case fails because the absolute bound is 1e-12.

Tolerances should be stable engineering decisions, not values repeatedly increased until a flaky test turns green. If the error comes from randomness, uncontrolled input order or a changed algorithm, investigate that cause and test the intended invariant directly.

Practice solution and acceptance checks

python

import pytest


def total(values):
    return sum(values)


def test_decimal_total():
    assert total([0.1, 0.2]) == pytest.approx(0.3, rel=1e-9, abs=1e-12)

def test_empty_total():
    assert total([]) == pytest.approx(0.0, rel=1e-9, abs=1e-12)
Reasoning
The solution adds the empty total. Its probe changes the function to add a tiny 1e-6 only for empty input. The first nonempty example remains correct, while the new zero case fails because the absolute bound is 1e-12. Tolerances should be stable engineering decisions, not values repeatedly increased until a flaky test turns green. If the error comes from randomness, uncontrolled input order or a changed algorithm, investigate that cause and test the intended invariant directly.
Open in local Python playground ↗

Interview reasoning and next step

Explain the difference between approximation and rounding, and describe the near-zero case.

Explain the difference between approximation and rounding, and describe the near-zero case. State what the tolerance accepts and which concrete defect it rejects. If asked about arrays, discuss using array-specific comparison helpers with shape and dtype checks where those are part of the contract.

Next use fixtures to construct fresh test inputs. Numerical reproducibility also depends on deterministic inputs, owned random generators and explicit data types; one approximate assertion cannot supply those missing controls.

Optional stretch — after the core task works: Introduce one deliberate implementation mistake and explain which assertion catches it. A green run alone is not evidence that every possible bug is covered.

Computer lab: https://applydjinn.com/typorithm/pytest-lab-20261106.zip. Run verify.py in an isolated environment. Worked examples and solutions pass, controlled counterexamples fail, and the new practice assertions reject a mutation missed by the baseline. Run --practice pyq-approx after changing the supplied exercise. Python executes on your computer or in the browser, without submitting code to a server. Native question progress is a separate learning record.

Choose absolute and relative error limits from the quantity being tested.