8000 Merge pull request #19316 from arubiales/quit_runtimewarning_moduleop_ · numpy/numpy@e02af0b · GitHub
[go: up one dir, main page]

Skip to content

Commit e02af0b

Browse files
authored
Merge pull request #19316 from arubiales/quit_runtimewarning_moduleop_
BUG: Fix warning problems of the mod operator
2 parents 64e62ef + e502de2 commit e02af0b

File tree

3 files changed

+51
-90
lines changed

3 files changed

+51
-90
lines changed

numpy/core/src/npymath/npy_math_internal.h.src

Lines changed: 22 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -398,8 +398,8 @@ NPY_INPLACE @type@ npy_@kind@@c@(@type@ x)
398398
/**end repeat1**/
399399

400400
/**begin repeat1
401-
* #kind = atan2,hypot,pow,copysign#
402-
* #KIND = ATAN2,HYPOT,POW,COPYSIGN#
401+
* #kind = atan2,hypot,pow,fmod,copysign#
402+
* #KIND = ATAN2,HYPOT,POW,FMOD,COPYSIGN#
403403
*/
404404
#ifdef @kind@@c@
405405
#undef @kind@@c@
@@ -412,32 +412,6 @@ NPY_INPLACE @type@ npy_@kind@@c@(@type@ x, @type@ y)
412412
#endif
413413
/**end repeat1**/
414414

415-
/**begin repeat1
416-
* #kind = fmod#
417-
* #KIND = FMOD#
418-
*/
419-
#ifdef @kind@@c@
420-
#undef @kind@@c@
421-
#endif
422-
#ifndef HAVE_MODF@C@
423-
NPY_INPLACE @type@
424-
npy_@kind@@c@(@type@ x, @type@ y)
425-
{
426-
int are_inputs_inf = (npy_isinf(x) && npy_isinf(y));
427-
/* force set invalid flag, doesnt raise by default on gcc < 8 */
428-
if (npy_isnan(x) || npy_isnan(y)) {
429-
npy_set_floatstatus_invalid();
430-
}
431-
if (are_inputs_inf || !y) {
432-
if (!npy_isnan(x)) {
433-
npy_set_floatstatus_invalid();
434-
}
435-
}
436-
return (@type@) npy_@kind@((double)x, (double) y);
437-
}
438-
#endif
439-
/**end repeat1**/
440-
441415
#ifdef modf@c@
442416
#undef modf@c@
443417
#endif
@@ -499,8 +473,8 @@ NPY_INPLACE @type@ npy_@kind@@c@(@type@ x)
499473
/**end repeat1**/
500474

501475
/**begin repeat1
502-
* #kind = atan2,hypot,pow,copysign#
503-
* #KIND = ATAN2,HYPOT,POW,COPYSIGN#
476+
* #kind = atan2,hypot,pow,fmod,copysign#
477+
* #KIND = ATAN2,HYPOT,POW,FMOD,COPYSIGN#
504478
*/
505479
#ifdef HAVE_@KIND@@C@
506480
NPY_INPLACE @type@ npy_@kind@@c@(@type@ x, @type@ y)
@@ -510,29 +484,6 @@ NPY_INPLACE @type@ npy_@kind@@c@(@type@ x, @type@ y)
510484
#endif
511485
/**end repeat1**/
512486

513-
/**begin repeat1
514-
* #kind = fmod#
515-
* #KIND = FMOD#
516-
*/
517-
#ifdef HAVE_FMOD@C@
518-
NPY_INPLACE @type@
519-
npy_@kind@@c@(@type@ x, @type@ y)
520-
{
521-
int are_inputs_inf = (npy_isinf(x) && npy_isinf(y));
522-
/* force set invalid flag, doesnt raise by default on gcc < 8 */
523-
if (npy_isnan(x) || npy_isnan(y)) {
524-
npy_set_floatstatus_invalid();
525-
}
526-
if (are_inputs_inf || !y) {
527-
if (!npy_isnan(x)) {
528-
npy_set_floatstatus_invalid();
529-
}
530-
}
531-
return @kind@@c@(x, y);
532-
}
533-
#endif
534-
/**end repeat1**/
535-
536487
#ifdef HAVE_MODF@C@
537488
NPY_INPLACE @type@ npy_modf@c@(@type@ x, @type@ *iptr)
538489
{
@@ -682,8 +633,14 @@ npy_remainder@c@(@type@ a, @type@ b)
682633
{
683634
@type@ mod;
684635
if (NPY_UNLIKELY(!b)) {
636+
/*
637+
* in2 == 0 (and not NaN): normal fmod will give the correct
638+
* result (always NaN). `divmod` may set additional FPE for the
639+
* division by zero creating an inf.
640+
*/
685641
mod = npy_fmod@c@(a, b);
686-
} else {
642+
}
643+
else {
687644
npy_divmod@c@(a, b, &mod);
688645
}
689646
return mod;
@@ -693,13 +650,14 @@ NPY_INPLACE @type@
693650
npy_floor_divide@c@(@type@ a, @type@ b) {
694651
@type@ div, mod;
695652
if (NPY_UNLIKELY(!b)) {
653+
/*
654+
* in2 == 0 (and not NaN): normal division will give the correct
655+
* result (Inf or NaN). `divmod` may set additional FPE for the modulo
656+
* evaluating to NaN.
657+
*/
696658
div = a / b;
697-
if (!a || npy_isnan(a)) {
698-
npy_set_floatstatus_invalid();
699-
} else {
700-
npy_set_floatstatus_divbyzero();
701-
}
702-
} else {
659+
}
660+
else {
703661
div = npy_divmod@c@(a, b, &mod);
704662
}
705663
return div;
@@ -715,27 +673,19 @@ npy_divmod@c@(@type@ a, @type@ b, @type@ *modulus)
715673
{
716674
@type@ div, mod, floordiv;
717675

718-
/* force set invalid flag, doesnt raise by default on gcc < 8 */
719-
if (npy_isnan(a) || npy_isnan(b)) {
720-
npy_set_floatstatus_invalid();
721-
}
722676
mod = npy_fmod@c@(a, b);
723677
if (NPY_UNLIKELY(!b)) {
724-
div = a / b;
725-
if (a && !npy_isnan(a)) {
726-
npy_set_floatstatus_divbyzero();
727-
}
728-
/* If b == 0, return result of fmod. For IEEE is nan */
678+
/* b == 0 (not NaN): return result of fmod. For IEEE is nan */
729679
*modulus = mod;
730-
return div;
680+
return a / b;
731681
}
732682

733683
/* a - mod should be very nearly an integer multiple of b */
734684
div = (a - mod) / b;
735685

736686
/* adjust fmod result to conform to Python convention of remainder */
737687
if (mod) {
738-
if ((b < 0) != (mod < 0)) {
688+
if (isless(b, 0) != isless(mod, 0)) {
739689
mod += b;
740690
div -= 1.0@c@;
741691
}
@@ -748,7 +698,7 @@ npy_divmod@c@(@type@ a, @type@ b, @type@ *modulus)
748698
/* snap quotient to nearest integral value */
749699
if (div) {
750700
floordiv = npy_floor@c@(div);
751-
if (div - floordiv > 0.5@c@)
701+
if (isgreater(div - floordiv, 0.5@c@))
752702
floordiv += 1.0@c@;
753703< 10000 div class="diff-text-inner"> }
754704
else {

numpy/core/src/umath/scalarmath.c.src

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -283,19 +283,13 @@ static void
283283

284284
static void
285285
@name@_ctype_floor_divide(@type@ a, @type@ b, @type@ *out) {
286-
@type@ mod;
287-
288-
if (!b) {
289-
*out = a / b;
290-
} else {
291-
*out = npy_divmod@c@(a, b, &mod);
292-
}
286+
*out = npy_floor_divide@c@(a, b);
293287
}
294288

295289

296290
static void
297291
@name@_ctype_remainder(@type@ a, @type@ b, @type@ *out) {
298-
npy_divmod@c@(a, b, out);
292+
*out = npy_remainder@c@(a, b);
299293
}
300294

301295

numpy/core/tests/test_umath.py

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -458,10 +458,15 @@ def test_floor_division_errors(self, dtype):
458458
# divide by zero error check
459459
with np.errstate(divide='raise', invalid='ignore'):
460460
assert_raises(FloatingPointError, np.floor_divide, fone, fzer)
461-
with np.errstate(invalid='raise'):
462-
assert_raises(FloatingPointError, np.floor_divide, fnan, fone)
463-
assert_raises(FloatingPointError, np.floor_divide, fone, fnan)
464-
assert_raises(FloatingPointError, np.floor_divide, fnan, fzer)
461+
with np.errstate(divide='ignore', invalid='raise'):
462+
np.floor_divide(fone, fzer)
463+
464+
# The following already contain a NaN and should not warn
465+
with np.errstate(all='raise'):
466+
np.floor_divide(fnan, fone)
467+
np.floor_divide(fone, fnan)
468+
np.floor_divide(fnan, fzer)
469+
np.floor_divide(fzer, fnan)
465470

466471
@pytest.mark.parametrize('dtype', np.typecodes['Float'])
467472
def test_floor_division_corner_cases(self, dtype):
@@ -558,6 +563,9 @@ def test_float_remainder_roundoff(self):
558563
else:
559564
assert_(b > rem >= 0, msg)
560565

566+
@pytest.mark.xfail(sys.platform.startswith("darwin"),
567+
reason="MacOS seems to not give the correct 'invalid' warning for "
568+
"`fmod`. Hopefully, others always do.")
561569
@pytest.mark.parametrize('dtype', np.typecodes['Float'])
562570
def test_float_divmod_errors(self, dtype):
563571
# Check valid errors raised for divmod and remainder
@@ -578,20 +586,29 @@ def test_float_divmod_errors(self, dtype):
578586
with np.errstate(divide='ignore', invalid='raise'):
579587
assert_raises(FloatingPointError, np.divmod, finf, fzero)
580588
with np.errstate(divide='raise', invalid='ignore'):
581-
assert_raises(FloatingPointError, np.divmod, finf, fzero)
589+
# inf / 0 does not set any flags, only the modulo creates a NaN
590+
np.divmod(finf, fzero)
582591

592+
@pytest.mark.xfail(sys.platform.startswith("darwin"),
593+
reason="MacOS seems to not give the correct 'invalid' warning for "
594+
"`fmod`. Hopefully, others always do.")
583595
@pytest.mark.parametrize('dtype', np.typecodes['Float'])
584596
@pytest.mark.parametrize('fn', [np.fmod, np.remainder])
585597
def test_float_remainder_errors(self, dtype, fn):
586598
fzero = np.array(0.0, dtype=dtype)
587599
fone = np.array(1.0, dtype=dtype)
588600
finf = np.array(np.inf, dtype=dtype)
589601
fnan = np.array(np.nan, dtype=dtype)
590-
with np.errstate(invalid='raise'):
591-
assert_raises(FloatingPointError, fn, fone, fzero)
592-
assert_raises(FloatingPointError, fn, fnan, fzero)
593-
assert_raises(FloatingPointError, fn, fone, fnan)
594-
assert_raises(FloatingPointError, fn, fnan, fone)
602+
603+
# The following already contain a NaN and should not warn.
604+
with np.errstate(all='raise'):
605+
with pytest.raises(FloatingPointError,
606+
match="invalid value"):
607+
fn(fone, fzero)
608+
fn(fnan, fzero)
609+
fn(fzero, fnan)
610+
fn(fone, fnan)
611+
fn(fnan, fone)
595612

596613
def test_float_remainder_overflow(self):
597614
a = np.finfo(np.float64).tiny

0 commit comments

Comments
 (0)
0