From cadfd83cb1e1df466eb91eb9c38dd2775455d64d Mon Sep 17 00:00:00 2001 From: Meekail Zain <34613774+Micky774@users.noreply.github.com> Date: Mon, 13 Jun 2022 16:55:04 -0400 Subject: ENH: issue overflow warning when using `abs` on `np.int8(-128)` (#21648) Checks condition a == NPY_MIN_@NAME@ to determine whether an overflow error has occurred for np.int8 type. See #21289 and #21188 (comment) for reference. This also adds error integer overflow handling to the `-scalar` paths and "activates" a test for the unsigned versions. A few tests are skipped, because the tests were buggy (they never ran). These paths require followups to fix. --- numpy/core/src/umath/scalarmath.c.src | 22 +++++++++++++++++++++- numpy/core/tests/test_scalarmath.py | 10 ++++++---- numpy/typing/tests/data/pass/arithmetic.py | 6 ++++-- 3 files changed, 31 insertions(+), 7 deletions(-) (limited to 'numpy') diff --git a/numpy/core/src/umath/scalarmath.c.src b/numpy/core/src/umath/scalarmath.c.src index 4993546f8..412f171ec 100644 --- a/numpy/core/src/umath/scalarmath.c.src +++ b/numpy/core/src/umath/scalarmath.c.src @@ -584,10 +584,15 @@ static NPY_INLINE int /**begin repeat * #name = byte, short, int, long, longlong# * #type = npy_byte, npy_short, npy_int, npy_long, npy_longlong# + * #NAME = BYTE, SHORT, INT, LONG, LONGLONG# */ static NPY_INLINE int @name@_ctype_absolute(@type@ a, @type@ *out) { + if (a == NPY_MIN_@NAME@) { + *out = a; + return NPY_FPE_OVERFLOW; + } *out = (a < 0 ? -a : a); return 0; } @@ -1564,8 +1569,23 @@ static PyObject * val = PyArrayScalar_VAL(a, @Name@); + int retstatus = @name@_ctype_@oper@(val, &out); - @name@_ctype_@oper@(val, &out); + if (retstatus) { + int bufsize, errmask; + PyObject *errobj; + + if (PyUFunc_GetPyValues("@name@_scalars", &bufsize, &errmask, + &errobj) < 0) { + return NULL; + } + int first = 1; + if (PyUFunc_handlefperr(errmask, errobj, retstatus, &first)) { + Py_XDECREF(errobj); + return NULL; + } + Py_XDECREF(errobj); + } /* * TODO: Complex absolute should check floating point flags. diff --git a/numpy/core/tests/test_scalarmath.py b/numpy/core/tests/test_scalarmath.py index b7fe5183e..57937a7a9 100644 --- a/numpy/core/tests/test_scalarmath.py +++ b/numpy/core/tests/test_scalarmath.py @@ -897,8 +897,11 @@ def test_scalar_integer_operation_overflow(dtype, operation): @pytest.mark.parametrize("dtype", np.typecodes["Integer"]) @pytest.mark.parametrize("operation", [ lambda min, neg_1: abs(min), - lambda min, neg_1: min * neg_1, - lambda min, neg_1: min // neg_1], ids=["abs", "*", "//"]) + pytest.param(lambda min, neg_1: min * neg_1, + marks=pytest.mark.xfail(reason="broken on some platforms")), + pytest.param(lambda min, neg_1: min // neg_1, + marks=pytest.mark.skip(reason="broken on some platforms"))], + ids=["abs", "*", "//"]) def test_scalar_signed_integer_overflow(dtype, operation): # The minimum signed integer can "overflow" for some additional operations st = np.dtype(dtype).type @@ -910,8 +913,7 @@ def test_scalar_signed_integer_overflow(dtype, operation): @pytest.mark.parametrize("dtype", np.typecodes["UnsignedInteger"]) -@pytest.mark.xfail # TODO: the check is quite simply missing! -def test_scalar_signed_integer_overflow(dtype): +def test_scalar_unsigned_integer_overflow(dtype): val = np.dtype(dtype).type(8) with pytest.warns(RuntimeWarning, match="overflow encountered"): -val diff --git a/numpy/typing/tests/data/pass/arithmetic.py b/numpy/typing/tests/data/pass/arithmetic.py index 4ed69c923..07a990127 100644 --- a/numpy/typing/tests/data/pass/arithmetic.py +++ b/numpy/typing/tests/data/pass/arithmetic.py @@ -2,6 +2,7 @@ from __future__ import annotations from typing import Any import numpy as np +import pytest c16 = np.complex128(1) f8 = np.float64(1) @@ -330,8 +331,9 @@ AR_O **= AR_LIKE_O -f4 -i8 -i4 --u8 --u4 +with pytest.warns(RuntimeWarning): + -u8 + -u4 -td -AR_f -- cgit v1.2.1