summaryrefslogtreecommitdiffstats
path: root/Python/bltinmodule.c
diff options
context:
space:
mode:
authorMark Dickinson <dickinsm@gmail.com>2009-11-18 19:33:35 (GMT)
committerMark Dickinson <dickinsm@gmail.com>2009-11-18 19:33:35 (GMT)
commitbd15a06fd3ac256d4f2780c85a9f7e6def1ecd1f (patch)
treed6f4201cd2881e1b646906b5923c51455d96be73 /Python/bltinmodule.c
parent0516f8138643ca49a6e5fd56e0aa546829465a37 (diff)
downloadcpython-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.c57
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,