Skip to content

Commit 8d49f7c

Browse files
authored
bpo-39434: Improve float __floordiv__ performance and error message (GH-18147)
1 parent 2a4903f commit 8d49f7c

File tree

2 files changed

+42
-31
lines changed

2 files changed

+42
-31
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
:term:`floor division` of float operation now has a better performance. Also
2+
the message of :exc:`ZeroDivisionError` for this operation is updated.
3+
Patch by Dong-hee Na.

Objects/floatobject.c

Lines changed: 39 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -611,64 +611,72 @@ float_rem(PyObject *v, PyObject *w)
611611
return PyFloat_FromDouble(mod);
612612
}
613613

614-
static PyObject *
615-
float_divmod(PyObject *v, PyObject *w)
614+
static void
615+
_float_div_mod(double vx, double wx, double *floordiv, double *mod)
616616
{
617-
double vx, wx;
618-
double div, mod, floordiv;
619-
CONVERT_TO_DOUBLE(v, vx);
620-
CONVERT_TO_DOUBLE(w, wx);
621-
if (wx == 0.0) {
622-
PyErr_SetString(PyExc_ZeroDivisionError, "float divmod()");
623-
return NULL;
624-
}
625-
mod = fmod(vx, wx);
617+
double div;
618+
*mod = fmod(vx, wx);
626619
/* fmod is typically exact, so vx-mod is *mathematically* an
627620
exact multiple of wx. But this is fp arithmetic, and fp
628621
vx - mod is an approximation; the result is that div may
629622
not be an exact integral value after the division, although
630623
it will always be very close to one.
631624
*/
632-
div = (vx - mod) / wx;
633-
if (mod) {
625+
div = (vx - *mod) / wx;
626+
if (*mod) {
634627
/* ensure the remainder has the same sign as the denominator */
635-
if ((wx < 0) != (mod < 0)) {
636-
mod += wx;
628+
if ((wx < 0) != (*mod < 0)) {
629+
*mod += wx;
637630
div -= 1.0;
638631
}
639632
}
640633
else {
641634
/* the remainder is zero, and in the presence of signed zeroes
642635
fmod returns different results across platforms; ensure
643636
it has the same sign as the denominator. */
644-
mod = copysign(0.0, wx);
637+
*mod = copysign(0.0, wx);
645638
}
646639
/* snap quotient to nearest integral value */
647640
if (div) {
648-
floordiv = floor(div);
649-
if (div - floordiv > 0.5)
650-
floordiv += 1.0;
641+
*floordiv = floor(div);
642+
if (div - *floordiv > 0.5) {
643+
*floordiv += 1.0;
644+
}
651645
}
652646
else {
653647
/* div is zero - get the same sign as the true quotient */
654-
floordiv = copysign(0.0, vx / wx); /* zero w/ sign of vx/wx */
648+
*floordiv = copysign(0.0, vx / wx); /* zero w/ sign of vx/wx */
655649
}
656-
return Py_BuildValue("(dd)", floordiv, mod);
650+
}
651+
652+
static PyObject *
653+
float_divmod(PyObject *v, PyObject *w)
654+
{
655+
double vx, wx;
656+
double mod, floordiv;
657+
CONVERT_TO_DOUBLE(v, vx);
658+
CONVERT_TO_DOUBLE(w, wx);
659+
if (wx == 0.0) {
660+
PyErr_SetString(PyExc_ZeroDivisionError, "float divmod()");
661+
return NULL;
662+
}
663+
_float_div_mod(vx, wx, &floordiv, &mod);
664+
return Py_BuildValue("(dd)", floordiv, mod);
657665
}
658666

659667
static PyObject *
660668
float_floor_div(PyObject *v, PyObject *w)
661669
{
662-
PyObject *t, *r;
663-
664-
t = float_divmod(v, w);
665-
if (t == NULL || t == Py_NotImplemented)
666-
return t;
667-
assert(PyTuple_CheckExact(t));
668-
r = PyTuple_GET_ITEM(t, 0);
669-
Py_INCREF(r);
670-
Py_DECREF(t);
671-
return r;
670+
double vx, wx;
671+
double mod, floordiv;
672+
CONVERT_TO_DOUBLE(v, vx);
673+
CONVERT_TO_DOUBLE(w, wx);
674+
if (wx == 0.0) {
675+
PyErr_SetString(PyExc_ZeroDivisionError, "float floor division by zero");
676+
return NULL;
677+
}
678+
_float_div_mod(vx, wx, &floordiv, &mod);
679+
return PyFloat_FromDouble(floordiv);
672680
}
673681

674682
/* determine whether x is an odd integer or not; assumes that

0 commit comments

Comments
 (0)