diff options
author | Mark Dickinson <dickinsm@gmail.com> | 2009-11-18 19:33:35 (GMT) |
---|---|---|
committer | Mark Dickinson <dickinsm@gmail.com> | 2009-11-18 19:33:35 (GMT) |
commit | bd15a06fd3ac256d4f2780c85a9f7e6def1ecd1f (patch) | |
tree | d6f4201cd2881e1b646906b5923c51455d96be73 /Python/bltinmodule.c | |
parent | 0516f8138643ca49a6e5fd56e0aa546829465a37 (diff) | |
download | cpython-bd15a06fd3ac256d4f2780c85a9f7e6def1ecd1f.zip cpython-bd15a06fd3ac256d4f2780c85a9f7e6def1ecd1f.tar.gz cpython-bd15a06fd3ac256d4f2780c85a9f7e6def1ecd1f.tar.bz2 |
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.
Diffstat (limited to 'Python/bltinmodule.c')
-rw-r--r-- | Python/bltinmodule.c | 57 |
1 files changed, 38 insertions, 19 deletions
diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index fcae58a..a8d8d97 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -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; - else - number *= f; - number = round(number); - 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; - 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, |