8000 Merge pull request #16161 from anirudh2290/ufunc_divide_error · numpy/numpy@24a4704 · GitHub
[go: up one dir, main page]

Skip to content

Commit 24a4704

Browse files
authored
Merge pull request #16161 from anirudh2290/ufunc_divide_error
BUG: Potential fix for divmod(1.0, 0.0) to raise divbyzero and return inf
2 parents ba77419 + f527a57 commit 24a4704

File tree

5 files changed

+266
-17
lines changed

5 files changed

+266
-17
lines changed

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

Lines changed: 95 additions & 7 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,fmod,copysign#
402-
* #KIND = ATAN2,HYPOT,POW,FMOD,COPYSIGN#
401+
* #kind = atan2,hypot,pow,copysign#
402+
* #KIND = ATAN2,HYPOT,POW,COPYSIGN#
403403
*/
404404
#ifdef @kind@@c@
405405
#undef @kind@@c@
@@ -412,6 +412,32 @@ 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+
415441
#ifdef modf@c@
416442
#undef modf@c@
417443
#endif
@@ -473,8 +499,8 @@ NPY_INPLACE @type@ npy_@kind@@c@(@type@ x)
473499
/**end repeat1**/
474500

475501
/**begin repeat1
476-
* #kind = atan2,hypot,pow,fmod,copysign#
477-
* #KIND = ATAN2,HYPOT,POW,FMOD,COPYSIGN#
502+
* #kind = atan2,hypot,pow,copysign#
503+
* #KIND = ATAN2,HYPOT,POW,COPYSIGN#
478504
*/
479505
#ifdef HAVE_@KIND@@C@
480506
NPY_INPLACE @type@ npy_@kind@@c@(@type@ x, @type@ y)
@@ -484,6 +510,29 @@ NPY_INPLACE @type@ npy_@kind@@c@(@type@ x, @type@ y)
484510
#endif
485511
/**end repeat1**/
486512

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+
487536
#ifdef HAVE_MODF@C@
488537
NPY_INPLACE @type@ npy_modf@c@(@type@ x, @type@ *iptr)
489538
{
@@ -624,6 +673,38 @@ NPY_INPLACE @type@ npy_logaddexp2@c@(@type@ x, @type@ y)
624673
}
625674
}
626675

676+
/*
677+
* Wrapper function for remainder edge cases
678+
* Internally calls npy_divmod*
679+
*/
680+
NPY_INPLACE @type@
681+
npy_remainder@c@(@type@ a, @type@ b)
682+
{
683+
@type@ mod;
684+
if (NPY_UNLIKELY(!b)) {
685+
mod = npy_fmod@c@(a, b);
686+
} else {
687+
npy_divmod@c@(a, b, &mod);
688+
}
689+
return mod;
690+
}
691+
692+
NPY_INPLACE @type@
693+
npy_floor_divide@c@(@type@ a, @type@ b) {
694+
@type@ div, mod;
695+
if (NPY_UNLIKELY(!b)) {
696+
div = a / b;
697+
if (!a || npy_isnan(a)) {
698+
npy_set_floatstatus_invalid();
699+
} else {
700+
npy_set_floatstatus_divbyzero();
701+
}
702+
} else {
703+
div = npy_divmod@c@(a, b, &mod);
704+
}
705+
return div;
706+
}
707+
627708
/*
628709
* Python version of divmod.
629710
*
@@ -634,12 +715,19 @@ npy_divmod@c@(@type@ a, @type@ b, @type@ *modulus)
634715
{
635716
@type@ div, mod, floordiv;
636717

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+
}
637722
mod = npy_fmod@c@(a, b);
638-
639-
if (!b) {
723+
if (NPY_UNLIKELY(!b)) {
724+
div = a / b;
725+
if (a && !npy_isnan(a)) {
726+
npy_set_floatstatus_divbyzero();
727+
}
640728
/* If b == 0, return result of fmod. For IEEE is nan */
641729
*modulus = mod;
642-
return mod;
730+
return div;
643731
}
644732

645733
/* a - mod should be very nearly an integer multiple of b */

numpy/core/src/umath/loops.c.src

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1955,8 +1955,7 @@ NPY_NO_EXPORT void
19551955
BINARY_LOOP {
19561956
const @type@ in1 = *(@type@ *)ip1;
19571957
const @type@ in2 = *(@type@ *)ip2;
1958-
@type@ mod;
1959-
*((@type@ *)op1) = npy_divmod@c@(in1, in2, &mod);
1958+
*((@type@ *)op1) = npy_floor_divide@c@(in1, in2);
19601959
}
19611960
}
19621961

@@ -1966,7 +1965,7 @@ NPY_NO_EXPORT void
19661965
BINARY_LOOP {
19671966
const @type@ in1 = *(@type@ *)ip1;
19681967
const @type@ in2 = *(@type@ *)ip2;
1969-
npy_divmod@c@(in1, in2, (@type@ *)op1);
1968+
*((@type@ *) op1) = npy_remainder@c@(in1, in2);
19701969
}
19711970
}
19721971

@@ -2306,8 +2305,13 @@ HALF_floor_divide(char **args, npy_intp const *dimensions, npy_intp const *steps
23062305
BINARY_LOOP {
23072306
const npy_half in1 = *(npy_half *)ip1;
23082307
const npy_half in2 = *(npy_half *)ip2;
2309-
npy_half mod;
2310-
*((npy_half *)op1) = npy_half_divmod(in1, in2, &mod);
2308+
2309+
float fh1 = npy_half_to_float(in1);
2310+
float fh2 = npy_half_to_float(in2);
2311+
float div;
2312+
2313+
div = npy_floor_dividef(fh1, fh2);
2314+
*((npy_half *)op1) = npy_float_to_half(div);
23112315
}
23122316
}
23132317

@@ -2317,7 +2321,11 @@ HALF_remainder(char **args, npy_intp const *dimensions, npy_intp const *steps, v
23172321
BINARY_LOOP {
23182322
const npy_half in1 = *(npy_half *)ip1;
23192323
const npy_half in2 = *(npy_half *)ip2;
2320-
npy_half_divmod(in1, in2, (npy_half *)op1);
2324+
float fh1 = npy_half_to_float(in1);
2325+
float fh2 = npy_half_to_float(in2);
2326+
float mod;
2327+
mod = npy_remainderf(fh1, fh2);
2328+
*((npy_half *)op1) = npy_float_to_half(mod);
23212329
}
23222330
}
23232331

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

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,11 @@ static void
285285
@name@_ctype_floor_divide(@type@ a, @type@ b, @type@ *out) {
< 10662 /td>
286286
@type@ mod;
287287

288-
*out = npy_divmod@c@(a, b, &mod);
288+
if (!b) {
289+
*out = a / b;
290+
} else {
291+
*out = npy_divmod@c@(a, b, &mod);
292+
}
289293
}
290294

291295

@@ -318,7 +322,11 @@ static void
318322
half_ctype_floor_divide(npy_half a, npy_half b, npy_half *out) {
319323
npy_half mod;
320324

321-
*out = npy_half_divmod(a, b, &mod);
325+
if (!b) {
326+
*out = a / b;
327+
} else {
328+
*out = npy_half_divmod(a, b, &mod);
329+
}
322330
}
323331

324332

numpy/core/tests/test_scalarmath.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,10 @@ def test_float_modulus_corner_cases(self):
276276
# Check nans, inf
277277
with suppress_warnings() as sup:
278278
sup.filter(RuntimeWarning, "invalid value encountered in remainder")
279+
sup.filter(RuntimeWarning, "divide by zero encountered in remainder")
280+
sup.filter(RuntimeWarning, "divide by zero encountered in floor_divide")
281+
sup.filter(RuntimeWarning, "divide by zero encountered in divmod")
282+
sup.filter(RuntimeWarning, "invalid value encountered in divmod")
279283
for dt in np.typecodes['Float']:
280284
fone = np.array(1.0, dtype=dt)
281285
fzer = np.array(0.0, dtype=dt)
@@ -290,6 +294,9 @@ def test_float_modulus_corner_cases(self):
290294
assert_(np.isnan(rem), 'dt: %s' % dt)
291295
rem = operator.mod(finf, fone)
292296
assert_(np.isnan(rem), 'dt: %s' % dt)
297+
for op in [floordiv_and_mod, divmod]:
298+
div, mod = op(fone, fzer)
299+
assert_(np.isinf(div)) and assert_(np.isnan(mod))
293300

294301
def test_inplace_floordiv_handling(self):
295302
# issue gh-12927

0 commit comments

Comments
 (0)
0