diff options
| author | Sebastian Berg <sebastian@sipsolutions.net> | 2022-04-04 11:49:19 -0700 |
|---|---|---|
| committer | Sebastian Berg <sebastian@sipsolutions.net> | 2022-06-13 09:36:57 -0700 |
| commit | 35dae708788106361a73ffd141d77f91151f483d (patch) | |
| tree | f6e96b0f80f4d5b597f535e532aa3dd7d28eb3db /numpy | |
| parent | 84fd4a5a0e064d1f6be7bc0c96663690faa0353b (diff) | |
| download | numpy-35dae708788106361a73ffd141d77f91151f483d.tar.gz | |
ENH: Add overflow check to float setitem
Diffstat (limited to 'numpy')
| -rw-r--r-- | numpy/core/src/multiarray/arraytypes.c.src | 51 | ||||
| -rw-r--r-- | numpy/core/tests/test_half.py | 4 | ||||
| -rw-r--r-- | numpy/core/tests/test_ufunc.py | 18 | ||||
| -rw-r--r-- | numpy/ma/tests/test_core.py | 6 |
4 files changed, 65 insertions, 14 deletions
diff --git a/numpy/core/src/multiarray/arraytypes.c.src b/numpy/core/src/multiarray/arraytypes.c.src index 62a96e371..ca06643ce 100644 --- a/numpy/core/src/multiarray/arraytypes.c.src +++ b/numpy/core/src/multiarray/arraytypes.c.src @@ -7,6 +7,7 @@ #define NPY_NO_DEPRECATED_API NPY_API_VERSION #define _MULTIARRAYMODULE +#define _UMATHMODULE #define _NPY_NO_DEPRECATIONS /* for NPY_CHAR */ #include "numpy/npy_common.h" @@ -37,6 +38,9 @@ #include "npy_buffer.h" #include "arraytypes.h" + +#include "umathmodule.h" + /* * Define a stack allocated dummy array with only the minimum information set: * 1. The descr, the main field interesting here. @@ -96,10 +100,32 @@ MyPyFloat_AsDouble(PyObject *obj) return ret; } + +static float +MyPyFloat_AsFloat(PyObject *obj) +{ + double d_val = MyPyFloat_AsDouble(obj); + float res = (float)d_val; + if (NPY_UNLIKELY(npy_isinf(res) && !npy_isinf(d_val))) { + if (PyUFunc_GiveFloatingpointErrors("cast", NPY_FPE_OVERFLOW) < 0) { + return -1; + } + } + return res; +} + + static npy_half MyPyFloat_AsHalf(PyObject *obj) { - return npy_double_to_half(MyPyFloat_AsDouble(obj)); + double d_val = MyPyFloat_AsDouble(obj); + npy_half res = npy_double_to_half(d_val); + if (NPY_UNLIKELY(npy_half_isinf(res) && !npy_isinf(d_val))) { + if (PyUFunc_GiveFloatingpointErrors("cast", NPY_FPE_OVERFLOW) < 0) { + return npy_double_to_half(-1.); + } + } + return res; } static PyObject * @@ -200,7 +226,7 @@ MyPyLong_AsUnsigned@Type@ (PyObject *obj) * MyPyFloat_FromHalf, PyFloat_FromDouble*2# * #func2 = PyObject_IsTrue, MyPyLong_AsLong*6, MyPyLong_AsUnsignedLong*2, * MyPyLong_AsLongLong, MyPyLong_AsUnsignedLongLong, - * MyPyFloat_AsHalf, MyPyFloat_AsDouble*2# + * MyPyFloat_AsHalf, MyPyFloat_AsFloat, MyPyFloat_AsDouble# * #type = npy_bool, * npy_byte, npy_ubyte, npy_short, npy_ushort, npy_int, * npy_long, npy_uint, npy_ulong, npy_longlong, npy_ulonglong, @@ -238,7 +264,6 @@ static int } else { temp = (@type@)@func2@(op); - // TODO: Need to check for inf, if input is not inf (for float)! } if (PyErr_Occurred()) { PyObject *type, *value, *traceback; @@ -364,6 +389,26 @@ static int } temp.real = (@ftype@) oop.real; temp.imag = (@ftype@) oop.imag; + +#if NPY_SIZEOF_@NAME@ < NPY_SIZEOF_CDOUBLE /* really just float... */ + /* Overflow could have occured converting double to float */ + if (NPY_UNLIKELY((npy_isinf(temp.real) && !npy_isinf(oop.real)) || + (npy_isinf(temp.imag) && !npy_isinf(oop.imag)))) { + int bufsize, errmask; + PyObject *errobj; + + if (PyUFunc_GetPyValues("assignment", &bufsize, &errmask, + &errobj) < 0) { + return -1; + } + int first = 1; + if (PyUFunc_handlefperr(errmask, errobj, NPY_FPE_OVERFLOW, &first)) { + Py_XDECREF(errobj); + return -1; + } + Py_XDECREF(errobj); + } +#endif } memcpy(ov, &temp, PyArray_DESCR(ap)->elsize); diff --git a/numpy/core/tests/test_half.py b/numpy/core/tests/test_half.py index b60f7ba9c..6743dfb51 100644 --- a/numpy/core/tests/test_half.py +++ b/numpy/core/tests/test_half.py @@ -104,9 +104,9 @@ class TestHalf: # Increase the float by a minimal value: if offset == "up": - f16s_float = np.nextafter(f16s_float, float_t(1e50)) + f16s_float = np.nextafter(f16s_float, float_t(np.inf)) elif offset == "down": - f16s_float = np.nextafter(f16s_float, float_t(-1e50)) + f16s_float = np.nextafter(f16s_float, float_t(-np.inf)) # Convert back to float16 and its bit pattern: res_patterns = f16s_float.astype(np.float16).view(np.uint16) diff --git a/numpy/core/tests/test_ufunc.py b/numpy/core/tests/test_ufunc.py index 1142d66f5..56ca7f4bd 100644 --- a/numpy/core/tests/test_ufunc.py +++ b/numpy/core/tests/test_ufunc.py @@ -666,20 +666,22 @@ class TestUfunc: for dt in (int, np.float16, np.float32, np.float64, np.longdouble): for v in (0, 1, 2, 7, 8, 9, 15, 16, 19, 127, 128, 1024, 1235): - tgt = dt(v * (v + 1) / 2) - d = np.arange(1, v + 1, dtype=dt) - # warning if sum overflows, which it does in float16 - overflow = not np.isfinite(tgt) - with warnings.catch_warnings(record=True) as w: - warnings.simplefilter("always") - assert_almost_equal(np.sum(d), tgt) + warnings.simplefilter("always", RuntimeWarning) + + tgt = dt(v * (v + 1) / 2) + overflow = not np.isfinite(tgt) assert_equal(len(w), 1 * overflow) - assert_almost_equal(np.sum(d[::-1]), tgt) + d = np.arange(1, v + 1, dtype=dt) + + assert_almost_equal(np.sum(d), tgt) assert_equal(len(w), 2 * overflow) + assert_almost_equal(np.sum(d[::-1]), tgt) + assert_equal(len(w), 3 * overflow) + d = np.ones(500, dtype=dt) assert_almost_equal(np.sum(d[::2]), 250.) assert_almost_equal(np.sum(d[1::2]), 250.) diff --git a/numpy/ma/tests/test_core.py b/numpy/ma/tests/test_core.py index 04540bc70..4fac897de 100644 --- a/numpy/ma/tests/test_core.py +++ b/numpy/ma/tests/test_core.py @@ -4164,7 +4164,11 @@ class TestMaskedArrayFunctions: # test that masked_where on a structured array sets a structured # mask (see issue #2972) a = np.zeros(10, dtype=[("A", "<f2"), ("B", "<f4")]) - am = np.ma.masked_where(a["A"] < 5, a) + with np.errstate(over="ignore"): + # NOTE: The float16 "uses" 1e20 as mask, which overflows to inf + # and warns. Unrelated to this test, but probably undesired. + # But NumPy previously did not warn for this overflow. + am = np.ma.masked_where(a["A"] < 5, a) assert_equal(am.mask.dtype.names, am.dtype.names) assert_equal(am["A"], np.ma.masked_array(np.zeros(10), np.ones(10))) |
