a % b == b when a is a very small negative number
jepler at unpythonic.net
jepler at unpythonic.net
Sun Nov 24 12:47:50 EST 2002
Welcome to the wonderful world of floating point!
The result of -1e-18 % (2*math.pi) is the same as (2*math.pi-1e-18).
But the closest representable number to that is the same as the closest
representable number to (2*math.pi).
You can see the same thing with this code:
>>> -1. % 1e30 == 1e30
True
This is the body of "float_rem" (implements float%float) in
Objects/floatobject.c:
static PyObject *
float_rem(PyObject *v, PyObject *w)
{
double vx, wx;
double mod;
CONVERT_TO_DOUBLE(v, vx);
CONVERT_TO_DOUBLE(w, wx);
if (wx == 0.0) {
PyErr_SetString(PyExc_ZeroDivisionError, "float modulo");
return NULL;
}
PyFPE_START_PROTECT("modulo", return 0)
mod = fmod(vx, wx);
/* note: checking mod*wx < 0 is incorrect -- underflows to
0 if wx < sqrt(smallest nonzero double) */
if (mod && ((wx < 0) != (mod < 0))) {
mod += wx;
}
PyFPE_END_PROTECT(mod)
return PyFloat_FromDouble(mod);
}
The problem code is
if (mod && ((wx < 0) != (mod < 0))) {
mod += wx;
fmod returned -1e-18, which triggers this test (result is nonzero and has
opposite sign from mod). mod+=wx == wx (since mod is so small compared to
wx), and that's the result you get.
I don't know if it's "saner" to write
if (mod && ((wx < 0) != (mod < 0))) {
mod += wx;
if(mod == wx) mod = 0;
though in your case it would make the "problem" go away. Tim Peters will
probably tell me why this is a bad idea if he reads the thread.
Jeff
More information about the Python-list
mailing list