summaryrefslogtreecommitdiff
path: root/numpy
diff options
context:
space:
mode:
authorMatti Picus <matti.picus@gmail.com>2021-07-14 19:22:15 +0300
committerGitHub <noreply@github.com>2021-07-14 19:22:15 +0300
commite02af0b2991171c39bb3013fa83bdbe727cd3cda (patch)
tree9c5726211fe4d83bb4f7d585e653e6eec36e1ad7 /numpy
parent64e62ef69babccb36cb2ee014225c6845b163c0e (diff)
parente502de26b8c84802e224e0bad66f9d179b51eab9 (diff)
downloadnumpy-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.src94
-rw-r--r--numpy/core/src/umath/scalarmath.c.src10
-rw-r--r--numpy/core/tests/test_umath.py37
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