Skip to content

bpo-39434: Improve float __floordiv__ performance and error message #18147

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 30, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
:term:`floor division` of float operation now has a better performance. Also
the message of :exc:`ZeroDivisionError` for this operation is updated.
Patch by Dong-hee Na.
70 changes: 39 additions & 31 deletions Objects/floatobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -611,64 +611,72 @@ float_rem(PyObject *v, PyObject *w)
return PyFloat_FromDouble(mod);
}

static PyObject *
float_divmod(PyObject *v, PyObject *w)
static void
_float_div_mod(double vx, double wx, double *floordiv, double *mod)
{
double vx, wx;
double div, mod, floordiv;
CONVERT_TO_DOUBLE(v, vx);
CONVERT_TO_DOUBLE(w, wx);
if (wx == 0.0) {
PyErr_SetString(PyExc_ZeroDivisionError, "float divmod()");
return NULL;
}
mod = fmod(vx, wx);
double div;
*mod = fmod(vx, wx);
/* fmod is typically exact, so vx-mod is *mathematically* an
exact multiple of wx. But this is fp arithmetic, and fp
vx - mod is an approximation; the result is that div may
not be an exact integral value after the division, although
it will always be very close to one.
*/
div = (vx - mod) / wx;
if (mod) {
div = (vx - *mod) / wx;
if (*mod) {
/* ensure the remainder has the same sign as the denominator */
if ((wx < 0) != (mod < 0)) {
mod += wx;
if ((wx < 0) != (*mod < 0)) {
*mod += wx;
div -= 1.0;
}
}
else {
/* the remainder is zero, and in the presence of signed zeroes
fmod returns different results across platforms; ensure
it has the same sign as the denominator. */
mod = copysign(0.0, wx);
*mod = copysign(0.0, wx);
}
/* snap quotient to nearest integral value */
if (div) {
floordiv = floor(div);
if (div - floordiv > 0.5)
floordiv += 1.0;
*floordiv = floor(div);
if (div - *floordiv > 0.5) {
*floordiv += 1.0;
}
}
else {
/* div is zero - get the same sign as the true quotient */
floordiv = copysign(0.0, vx / wx); /* zero w/ sign of vx/wx */
*floordiv = copysign(0.0, vx / wx); /* zero w/ sign of vx/wx */
}
return Py_BuildValue("(dd)", floordiv, mod);
}

static PyObject *
float_divmod(PyObject *v, PyObject *w)
{
double vx, wx;
double mod, floordiv;
CONVERT_TO_DOUBLE(v, vx);
CONVERT_TO_DOUBLE(w, wx);
if (wx == 0.0) {
PyErr_SetString(PyExc_ZeroDivisionError, "float divmod()");
return NULL;
}
_float_div_mod(vx, wx, &floordiv, &mod);
return Py_BuildValue("(dd)", floordiv, mod);
}

static PyObject *
float_floor_div(PyObject *v, PyObject *w)
{
PyObject *t, *r;

t = float_divmod(v, w);
if (t == NULL || t == Py_NotImplemented)
return t;
assert(PyTuple_CheckExact(t));
r = PyTuple_GET_ITEM(t, 0);
Py_INCREF(r);
Py_DECREF(t);
return r;
double vx, wx;
double mod, floordiv;
CONVERT_TO_DOUBLE(v, vx);
CONVERT_TO_DOUBLE(w, wx);
if (wx == 0.0) {
PyErr_SetString(PyExc_ZeroDivisionError, "float floor division by zero");
return NULL;
}
_float_div_mod(vx, wx, &floordiv, &mod);
return PyFloat_FromDouble(floordiv);
}

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