diff options
| author | Matti Picus <matti.picus@gmail.com> | 2021-07-14 19:22:15 +0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-07-14 19:22:15 +0300 |
| commit | e02af0b2991171c39bb3013fa83bdbe727cd3cda (patch) | |
| tree | 9c5726211fe4d83bb4f7d585e653e6eec36e1ad7 /numpy | |
| parent | 64e62ef69babccb36cb2ee014225c6845b163c0e (diff) | |
| parent | e502de26b8c84802e224e0bad66f9d179b51eab9 (diff) | |
| download | numpy-e02af0b2991171c39bb3013fa83bdbe727cd3cda.tar.gz | |
Merge pull request #19316 from arubiales/quit_runtimewarning_moduleop_
BUG: Fix warning problems of the mod operator
Diffstat (limited to 'numpy')
| -rw-r--r-- | numpy/core/src/npymath/npy_math_internal.h.src | 94 | ||||
| -rw-r--r-- | numpy/core/src/umath/scalarmath.c.src | 10 | ||||
| -rw-r--r-- | numpy/core/tests/test_umath.py | 37 |
3 files changed, 51 insertions, 90 deletions
diff --git a/numpy/core/src/npymath/npy_math_internal.h.src b/numpy/core/src/npymath/npy_math_internal.h.src index ff4663dc3..1e46a2303 100644 --- a/numpy/core/src/npymath/npy_math_internal.h.src +++ b/numpy/core/src/npymath/npy_math_internal.h.src @@ -398,8 +398,8 @@ NPY_INPLACE @type@ npy_@kind@@c@(@type@ x) /**end repeat1**/ /**begin repeat1 - * #kind = atan2,hypot,pow,copysign# - * #KIND = ATAN2,HYPOT,POW,COPYSIGN# + * #kind = atan2,hypot,pow,fmod,copysign# + * #KIND = ATAN2,HYPOT,POW,FMOD,COPYSIGN# */ #ifdef @kind@@c@ #undef @kind@@c@ @@ -412,32 +412,6 @@ NPY_INPLACE @type@ npy_@kind@@c@(@type@ x, @type@ y) #endif /**end repeat1**/ -/**begin repeat1 - * #kind = fmod# - * #KIND = FMOD# - */ -#ifdef @kind@@c@ -#undef @kind@@c@ -#endif -#ifndef HAVE_MODF@C@ -NPY_INPLACE @type@ -npy_@kind@@c@(@type@ x, @type@ y) -{ - int are_inputs_inf = (npy_isinf(x) && npy_isinf(y)); - /* force set invalid flag, doesnt raise by default on gcc < 8 */ - if (npy_isnan(x) || npy_isnan(y)) { - npy_set_floatstatus_invalid(); - } - if (are_inputs_inf || !y) { - if (!npy_isnan(x)) { - npy_set_floatstatus_invalid(); - } - } - return (@type@) npy_@kind@((double)x, (double) y); -} -#endif -/**end repeat1**/ - #ifdef modf@c@ #undef modf@c@ #endif @@ -499,8 +473,8 @@ NPY_INPLACE @type@ npy_@kind@@c@(@type@ x) /**end repeat1**/ /**begin repeat1 - * #kind = atan2,hypot,pow,copysign# - * #KIND = ATAN2,HYPOT,POW,COPYSIGN# + * #kind = atan2,hypot,pow,fmod,copysign# + * #KIND = ATAN2,HYPOT,POW,FMOD,COPYSIGN# */ #ifdef HAVE_@KIND@@C@ NPY_INPLACE @type@ npy_@kind@@c@(@type@ x, @type@ y) @@ -510,29 +484,6 @@ NPY_INPLACE @type@ npy_@kind@@c@(@type@ x, @type@ y) #endif /**end repeat1**/ -/**begin repeat1 - * #kind = fmod# - * #KIND = FMOD# - */ -#ifdef HAVE_FMOD@C@ -NPY_INPLACE @type@ -npy_@kind@@c@(@type@ x, @type@ y) -{ - int are_inputs_inf = (npy_isinf(x) && npy_isinf(y)); - /* force set invalid flag, doesnt raise by default on gcc < 8 */ - if (npy_isnan(x) || npy_isnan(y)) { - npy_set_floatstatus_invalid(); - } - if (are_inputs_inf || !y) { - if (!npy_isnan(x)) { - npy_set_floatstatus_invalid(); - } - } - return @kind@@c@(x, y); -} -#endif -/**end repeat1**/ - #ifdef HAVE_MODF@C@ NPY_INPLACE @type@ npy_modf@c@(@type@ x, @type@ *iptr) { @@ -682,8 +633,14 @@ npy_remainder@c@(@type@ a, @type@ b) { @type@ mod; if (NPY_UNLIKELY(!b)) { + /* + * in2 == 0 (and not NaN): normal fmod will give the correct + * result (always NaN). `divmod` may set additional FPE for the + * division by zero creating an inf. + */ mod = npy_fmod@c@(a, b); - } else { + } + else { npy_divmod@c@(a, b, &mod); } return mod; @@ -693,13 +650,14 @@ NPY_INPLACE @type@ npy_floor_divide@c@(@type@ a, @type@ b) { @type@ div, mod; if (NPY_UNLIKELY(!b)) { + /* + * in2 == 0 (and not NaN): normal division will give the correct + * result (Inf or NaN). `divmod` may set additional FPE for the modulo + * evaluating to NaN. + */ div = a / b; - if (!a || npy_isnan(a)) { - npy_set_floatstatus_invalid(); - } else { - npy_set_floatstatus_divbyzero(); - } - } else { + } + else { div = npy_divmod@c@(a, b, &mod); } return div; @@ -715,19 +673,11 @@ npy_divmod@c@(@type@ a, @type@ b, @type@ *modulus) { @type@ div, mod, floordiv; - /* force set invalid flag, doesnt raise by default on gcc < 8 */ - if (npy_isnan(a) || npy_isnan(b)) { - npy_set_floatstatus_invalid(); - } mod = npy_fmod@c@(a, b); if (NPY_UNLIKELY(!b)) { - div = a / b; - if (a && !npy_isnan(a)) { - npy_set_floatstatus_divbyzero(); - } - /* If b == 0, return result of fmod. For IEEE is nan */ + /* b == 0 (not NaN): return result of fmod. For IEEE is nan */ *modulus = mod; - return div; + return a / b; } /* a - mod should be very nearly an integer multiple of b */ @@ -735,7 +685,7 @@ npy_divmod@c@(@type@ a, @type@ b, @type@ *modulus) /* adjust fmod result to conform to Python convention of remainder */ if (mod) { - if ((b < 0) != (mod < 0)) { + if (isless(b, 0) != isless(mod, 0)) { mod += b; div -= 1.0@c@; } @@ -748,7 +698,7 @@ npy_divmod@c@(@type@ a, @type@ b, @type@ *modulus) /* snap quotient to nearest integral value */ if (div) { floordiv = npy_floor@c@(div); - if (div - floordiv > 0.5@c@) + if (isgreater(div - floordiv, 0.5@c@)) floordiv += 1.0@c@; } else { diff --git a/numpy/core/src/umath/scalarmath.c.src b/numpy/core/src/umath/scalarmath.c.src index 66f97a831..5836545f8 100644 --- a/numpy/core/src/umath/scalarmath.c.src +++ b/numpy/core/src/umath/scalarmath.c.src @@ -283,19 +283,13 @@ static void static void @name@_ctype_floor_divide(@type@ a, @type@ b, @type@ *out) { - @type@ mod; - - if (!b) { - *out = a / b; - } else { - *out = npy_divmod@c@(a, b, &mod); - } + *out = npy_floor_divide@c@(a, b); } static void @name@_ctype_remainder(@type@ a, @type@ b, @type@ *out) { - npy_divmod@c@(a, b, out); + *out = npy_remainder@c@(a, b); } diff --git a/numpy/core/tests/test_umath.py b/numpy/core/tests/test_umath.py index 6fd4b4659..ebd61d199 100644 --- a/numpy/core/tests/test_umath.py +++ b/numpy/core/tests/test_umath.py @@ -458,10 +458,15 @@ class TestDivision: # divide by zero error check with np.errstate(divide='raise', invalid='ignore'): assert_raises(FloatingPointError, np.floor_divide, fone, fzer) - with np.errstate(invalid='raise'): - assert_raises(FloatingPointError, np.floor_divide, fnan, fone) - assert_raises(FloatingPointError, np.floor_divide, fone, fnan) - assert_raises(FloatingPointError, np.floor_divide, fnan, fzer) + with np.errstate(divide='ignore', invalid='raise'): + np.floor_divide(fone, fzer) + + # The following already contain a NaN and should not warn + with np.errstate(all='raise'): + np.floor_divide(fnan, fone) + np.floor_divide(fone, fnan) + np.floor_divide(fnan, fzer) + np.floor_divide(fzer, fnan) @pytest.mark.parametrize('dtype', np.typecodes['Float']) def test_floor_division_corner_cases(self, dtype): @@ -558,6 +563,9 @@ class TestRemainder: else: assert_(b > rem >= 0, msg) + @pytest.mark.xfail(sys.platform.startswith("darwin"), + reason="MacOS seems to not give the correct 'invalid' warning for " + "`fmod`. Hopefully, others always do.") @pytest.mark.parametrize('dtype', np.typecodes['Float']) def test_float_divmod_errors(self, dtype): # Check valid errors raised for divmod and remainder @@ -578,8 +586,12 @@ class TestRemainder: with np.errstate(divide='ignore', invalid='raise'): assert_raises(FloatingPointError, np.divmod, finf, fzero) with np.errstate(divide='raise', invalid='ignore'): - assert_raises(FloatingPointError, np.divmod, finf, fzero) + # inf / 0 does not set any flags, only the modulo creates a NaN + np.divmod(finf, fzero) + @pytest.mark.xfail(sys.platform.startswith("darwin"), + reason="MacOS seems to not give the correct 'invalid' warning for " + "`fmod`. Hopefully, others always do.") @pytest.mark.parametrize('dtype', np.typecodes['Float']) @pytest.mark.parametrize('fn', [np.fmod, np.remainder]) def test_float_remainder_errors(self, dtype, fn): @@ -587,11 +599,16 @@ class TestRemainder: fone = np.array(1.0, dtype=dtype) finf = np.array(np.inf, dtype=dtype) fnan = np.array(np.nan, dtype=dtype) - with np.errstate(invalid='raise'): - assert_raises(FloatingPointError, fn, fone, fzero) - assert_raises(FloatingPointError, fn, fnan, fzero) - assert_raises(FloatingPointError, fn, fone, fnan) - assert_raises(FloatingPointError, fn, fnan, fone) + + # The following already contain a NaN and should not warn. + with np.errstate(all='raise'): + with pytest.raises(FloatingPointError, + match="invalid value"): + fn(fone, fzero) + fn(fnan, fzero) + fn(fzero, fnan) + fn(fone, fnan) + fn(fnan, fone) def test_float_remainder_overflow(self): a = np.finfo(np.float64).tiny |
