pytest assert almost equal
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
When testing floating-point calculations in Python, exact equality comparisons often fail due to precision errors inherent in IEEE 754 representation. For example, 0.1 + 0.2 equals 0.30000000000000004, not 0.3. Pytest provides pytest.approx() to handle approximate comparisons cleanly and readably. It works with scalars, lists, dicts, and NumPy arrays, and supports both relative and absolute tolerance.
The Floating-Point Problem
Floating-point numbers are stored in binary, and many decimal fractions (like 0.1) have infinite binary representations that get truncated. This makes exact comparison unreliable for computed values.
Using pytest.approx()
pytest.approx(expected) wraps the expected value and compares using a default relative tolerance of 1e-6. The assertion reads naturally — the == operator is overloaded to perform approximate comparison.
Relative vs Absolute Tolerance
Relative tolerance scales with the expected value, making it suitable for large numbers. Absolute tolerance is a fixed threshold, necessary when comparing values near zero where relative tolerance produces zero.
Comparing Collections
pytest.approx() works element-wise on flat lists, tuples, and dicts. It does not support nested collections — for those, compare each level separately.
Using with NumPy
Both pytest.approx() and np.testing.assert_allclose() work for NumPy arrays. assert_allclose provides more detailed error messages showing which elements differ.
Alternatives to pytest.approx
| Method | Works With | Tolerance Type | Error Messages |
pytest.approx() | scalars, lists, dicts, numpy | rel + abs | Clear, detailed |
math.isclose() | scalars only | rel + abs | Boolean only |
assertAlmostEqual() | scalars only | decimal places | unittest style |
np.testing.assert_allclose() | numpy arrays | rel + abs | Shows mismatched elements |
Common Pitfalls
- Using
==withoutpytest.approx()for floats: Bareassert result == 0.3fails for computed floating-point values. Always wrap the expected value inpytest.approx()when comparing floats in tests. - Relying on relative tolerance near zero: When the expected value is zero,
rel * |expected|equals zero, so relative tolerance alone always fails. Useabstolerance for values near zero:pytest.approx(0, abs=1e-9). - Comparing nested structures with
pytest.approx():pytest.approx()does not recurse into nested lists or dicts.[[0.3]]insidepytest.approx()raises aTypeError. Flatten the comparison or compare each level separately. - Confusing
relandabsinteraction: When bothrelandabsare specified, the comparison passes if either tolerance is satisfied (logical OR). This means the effective tolerance is the larger of the two, not the smaller. - Forgetting that
assertAlmostEqualuses decimal places, not tolerance:self.assertAlmostEqual(a, b, places=7)checks thatabs(a - b) < 5e-8(half a unit in the 7th decimal place). This is different from relative tolerance and can be surprising for very large or very small numbers.
Summary
- Use
pytest.approx(expected)for floating-point comparisons in pytest - Default relative tolerance is
1e-6; useabsfor values near zero - Works with scalars, flat lists, flat dicts, and NumPy arrays
math.isclose()is the standard library alternative for scalar comparisonsnp.testing.assert_allclose()provides detailed NumPy array comparison- Never use bare
==for computed floating-point values in tests

