You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
pytest.approx with a list expected value reports overflowing differences for numpy integer arrays #15314
Comparing a NumPy integer array against pytest.approx(<python list>) uses 8-bit arithmetic in the failure report, so the reported differences are wrong and, with -Werror, the report degrades to representation of details failed. The approx(<numpy array>) path unboxes the NumPy scalars (ApproxNumpy._yield_comparisons / _repr_compare), the sequence path (ApproxSequenceLike) does not.
E RuntimeWarning: overflow encountered in scalar subtract
result: bool = abs(self.expected - actual) <= self.tolerance
Without -Werror the test fails, but the message reports the wrong numbers:
E AssertionError:
E assert array([255], dtype=uint8) == approx([0 ± 1.0e-12])
E comparison failed. Mismatched elements: 1 / 1:
E Max absolute difference: 1
E Max relative difference: 0.00392156862745098
The differences come from ApproxSequenceLike._repr_compare subtracting a numpy.uint8 scalar from a Python float, which wraps around (255 - 0 → -1), so the absolute difference is 1 instead of 255. int8 is worse: pytest.approx([127]) against np.array([-128], dtype=np.int8) reports Max relative difference: -0.0078125.
pytest.approx(np.array([0], dtype=np.uint8)) against the same array reports 255 and 1.0 correctly, so the two approx flavours disagree about the same comparison.
Unboxing the value in the sequence loop as well fixes it:
It is a no-op for plain Python values, since _unbox_numpy_scalar only calls .item() when the attribute exists. With that applied both reproducers report 255 / 1.0 and testing/python/approx.py still passes.
Related: #15312 fixes the array-expected path only.
Thanks for the report. I reproduced this on main (numpy 2.5.4), and it is broader than the failure report. The equality check itself is wrong for numpy integer scalars against anything other than approx(<ndarray>):
a=np.uint8(255)
a==pytest.approx(254, abs=2) # False, should be Truenp.array([a]) ==pytest.approx([254], abs=2) # False, should be True
{"a": a} ==pytest.approx({"a": 254}, abs=2) # False, should be True
The cause is ApproxScalar.__eq__ evaluating self.expected - actual with a raw numpy.uint8, so 254 - uint8(255) wraps to 255. The approx(<ndarray>) path avoids this because _yield_comparisons already unboxes via _unbox_numpy_scalar.
Unboxing only in ApproxSequenceLike._repr_compare fixes the report for lists but leaves equality wrong, and -Werror still fails at approx.py:602 before the report is built.
Side note: the mapping report divides by the expected value for the relative difference while the sequence report divides by the actual value. That is pre-existing and I haven't changed it.
I'd like to work on this — the fix looks like unboxing numpy scalars on the actual side in ApproxSequenceLike (same as ApproxNumpy._yield_comparisons already does), for both the equality check and the failure report.
Activity
doubledare704 commentedon Nov 1, 2026
Thanks for the report. I reproduced this on
main(numpy 2.5.4), and it is broader than the failure report. The equality check itself is wrong for numpy integer scalars against anything other thanapprox(<ndarray>):The cause is
ApproxScalar.__eq__evaluatingself.expected - actualwith a rawnumpy.uint8, so254 - uint8(255)wraps to 255. Theapprox(<ndarray>)path avoids this because_yield_comparisonsalready unboxes via_unbox_numpy_scalar.Unboxing only in
ApproxSequenceLike._repr_comparefixes the report for lists but leaves equality wrong, and-Werrorstill fails atapprox.py:602before the report is built.I have a fix that unboxes
actualonce inApproxScalar.__eq__and also in the sequence and mapping_repr_compare, with tests for list, tuple, dict and scalar undernp.errstate(all="raise"): https://github.com/doubledare704/pytest/tree/fix-approx-numpy-int-overflowI'll open a PR from it.
Side note: the mapping report divides by the expected value for the relative difference while the sequence report divides by the actual value. That is pre-existing and I haven't changed it.
rinopatrick commentedon Nov 1, 2026
I'd like to work on this — the fix looks like unboxing numpy scalars on the actual side in
ApproxSequenceLike(same asApproxNumpy._yield_comparisonsalready does), for both the equality check and the failure report.