8000 BUG: Fix floating point error flags in division related ops · numpy/numpy@ebf46a2 · GitHub
[go: up one dir, main page]

Skip to content

Commit ebf46a2

Browse files
committed
BUG: Fix floating point error flags in division related ops
1 parent 8eefdd1 commit ebf46a2

File tree

3 files changed

+30
-73
lines changed

3 files changed

+30
-73
lines changed

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

Lines changed: 24 additions & 64 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,29 +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-
428-
if (are_inputs_inf || !y) {
429-
if (!npy_isnan(x)) {
430-
npy_set_floatstatus_invalid();
431-
}
432-
}
433-
return (@type@) npy_@kind@((double)x, (double) y);
434-
}
435-
#endif
436-
/**end repeat1**/
437-
438415
#ifdef modf@c@
439416
#undef modf@c@
440417
#endif
@@ -496,8 +473,8 @@ NPY_INPLACE @type@ npy_@kind@@c@(@type@ x)
496473
/**end repeat1**/
497474

498475
/**begin repeat1
499-
* #kind = atan2,hypot,pow,copysign#
500-
* #KIND = ATAN2,HYPOT,POW,COPYSIGN#
476+
* #kind = atan2,hypot,pow,fmod,copysign#
477+
* #KIND = ATAN2,HYPOT,POW,FMOD,COPYSIGN#
501478
*/
502479
#ifdef HAVE_@KIND@@C@
503480
NPY_INPLACE @type@ npy_@kind@@c@(@type@ x, @type@ y)
@@ -507,26 +484,6 @@ NPY_INPLACE @type@ npy_@kind@@c@(@type@ x, @type@ y)
507484
#endif
508485
/**end repeat1**/
509486

510-
/**begin repeat1
511-
* #kind = fmod#
512-
* #KIND = FMOD#
513-
*/
514-
#ifdef HAVE_FMOD@C@
515-
NPY_INPLACE @type@
516-
npy_@kind@@c@(@type@ x, @type@ y)
517-
{
518-
int are_inputs_inf = (npy_isinf(x) && npy_isinf(y));
519-
520-
if (are_inputs_inf || !y) {
521-
if (!npy_isnan(x)) {
522-
npy_set_floatstatus_invalid();
523-
}
524-
}
525-
return @kind@@c@(x, y);
526-
}
527-
#endif
528-
/**end repeat1**/
529-
530487
#ifdef HAVE_MODF@C@
531488
NPY_INPLACE @type@ npy_modf@c@(@type@ x, @type@ *iptr)
532489
{
@@ -676,25 +633,32 @@ npy_remainder@c@(@type@ a, @type@ b)
676633
{
677634
@type@ mod;
678635
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+
*/
679641
mod = npy_fmod@c@(a, b);
680-
} else {
642+
}
643+
else {
681644
npy_divmod@c@(a, b, &mod);
682645
}
683646
return mod;
684647
}
685648

686649
NPY_INPLACE @type@
687650
npy_floor_divide@c@(@type@ a, @type@ b) {
688-
@type@ div, mod;
651+
@type@ mod;
689652
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+
*/
690658
div = a / b;
691-
if (!a || npy_isnan(a)) {
692-
npy_set_floatstatus_invalid();
693-
} else {
694-
npy_set_flo F438 atstatus_divbyzero();
695-
}
696-
} else {
697-
div = npy_divmod@c@(a, b, &mod);
659+
}
660+
else {
661+
div = npy_divmod@c@(a, b, &mod);;
698662
}
699663
return div;
700664
}
@@ -711,21 +675,17 @@ npy_divmod@c@(@type@ a, @type@ b, @type@ *modulus)
711675

712676
mod = npy_fmod@c@(a, b);
713677
if (NPY_UNLIKELY(!b)) {
714-
div = a / b;
715-
if (a && !npy_isnan(a)) {
716-
npy_set_floatstatus_divbyzero();
717-
}
718-
/* If b == 0, return result of fmod. For IEEE is nan */
678+
/* b == 0 (not NaN): return result of fmod. For IEEE is nan */
719679
*modulus = mod;
720-
return div;
680+
return a / b;
721681
}
722682

723683
/* a - mod should be very nearly an integer multiple of b */
724684
div = (a - mod) / b;
725685

726686
/* adjust fmod result to conform to Python convention of remainder */
727687
if (mod) {
728-
if ((b < 0) != (mod < 0)) {
688+
if (isless(b, 0) != isless(mod, 0)) {
729689
mod += b;
730690
div -= 1.0@c@;
731691
}
@@ -738,7 +698,7 @@ npy_divmod@c@(@type@ a, @type@ b, @type@ *modulus)
738698
/* snap quotient to nearest integral value */
739699
if (div) {
740700
floordiv = npy_floor@c@(div);
741-
if (div - floordiv > 0.5@c@)
701+
if (isgreater(div - floordiv, 0.5@c@))
742702
floordiv += 1.0@c@;
743703
}
744704
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: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,8 @@ 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(divide='ignore', invalid='raise'):
462+
np.floor_divide(fone, fzer)
461463

462464
# The following already contain a NaN and should not warn
463465
with np.errstate(all='raise'):
@@ -581,7 +583,8 @@ def test_float_divmod_errors(self, dtype):
581583
with np.errstate(divide='ignore', invalid='raise'):
582584
assert_raises(FloatingPointError, np.divmod, finf, fzero)
583585
with np.errstate(divide='raise', invalid='ignore'):
584-
assert_raises(FloatingPointError, np.divmod, finf, fzero)
586+
# inf / 0 does not set any flags, only the modulo creates a NaN
587+
np.divmod(finf, fzero)
585588

586589
@pytest.mark.parametrize('dtype', np.typecodes['Float'])
587590
@pytest.mark.parametrize('fn', [np.fmod, np.remainder])

0 commit comments

Comments
 (0)
0