Issue #7117, continued: Change round implementation to use the correctly-rounded

string <-> float conversions;  this makes sure that the result of the round
operation is correctly rounded, and hence displays nicely using the new float
repr.
This commit is contained in:
Mark Dickinson 2009-11-18 19:33:35 +00:00
parent 0516f81386
commit bd15a06fd3
5 changed files with 389 additions and 20 deletions

View file

@ -8,6 +8,7 @@
#include "eval.h"
#include <ctype.h>
#include <float.h> /* for DBL_MANT_DIG and friends */
#ifdef RISCOS
#include "unixstuff.h"
@ -2120,29 +2121,47 @@ For most object types, eval(repr(object)) == object.");
static PyObject *
builtin_round(PyObject *self, PyObject *args, PyObject *kwds)
{
double number;
double f;
int ndigits = 0;
int i;
double x;
PyObject *o_ndigits = NULL;
Py_ssize_t ndigits;
static char *kwlist[] = {"number", "ndigits", 0};
if (!PyArg_ParseTupleAndKeywords(args, kwds, "d|i:round",
kwlist, &number, &ndigits))
if (!PyArg_ParseTupleAndKeywords(args, kwds, "d|O:round",
kwlist, &x, &o_ndigits))
return NULL;
f = 1.0;
i = abs(ndigits);
while (--i >= 0)
f = f*10.0;
if (ndigits < 0)
number /= f;
/* nans, infinities and zeros round to themselves */
if (!Py_IS_FINITE(x) || x == 0.0)
return PyFloat_FromDouble(x);
if (o_ndigits == NULL) {
/* second argument defaults to 0 */
ndigits = 0;
}
else {
/* interpret 2nd argument as a Py_ssize_t; clip on overflow */
ndigits = PyNumber_AsSsize_t(o_ndigits, NULL);
if (ndigits == -1 && PyErr_Occurred())
return NULL;
}
/* Deal with extreme values for ndigits. For ndigits > NDIGITS_MAX, x
always rounds to itself. For ndigits < NDIGITS_MIN, x always
rounds to +-0.0. Here 0.30103 is an upper bound for log10(2). */
#define NDIGITS_MAX ((int)((DBL_MANT_DIG-DBL_MIN_EXP) * 0.30103))
#define NDIGITS_MIN (-(int)((DBL_MAX_EXP + 1) * 0.30103))
if (ndigits > NDIGITS_MAX)
/* return x */
return PyFloat_FromDouble(x);
else if (ndigits < NDIGITS_MIN)
/* return 0.0, but with sign of x */
return PyFloat_FromDouble(0.0*x);
else
number *= f;
number = round(number);
if (ndigits < 0)
number *= f;
else
number /= f;
return PyFloat_FromDouble(number);
/* finite x, and ndigits is not unreasonably large */
/* _Py_double_round is defined in floatobject.c */
return _Py_double_round(x, (int)ndigits);
#undef NDIGITS_MAX
#undef NDIGITS_MIN
}
PyDoc_STRVAR(round_doc,