summaryrefslogtreecommitdiffstats
path: root/Objects/intobject.c
diff options
context:
space:
mode:
authorMark Dickinson <dickinsm@gmail.com>2009-04-20 21:13:33 (GMT)
committerMark Dickinson <dickinsm@gmail.com>2009-04-20 21:13:33 (GMT)
commit6736cf8d20b67b74e8e959622132963285156242 (patch)
tree6527638e42304a987b3ebc1541e4b75a3a838500 /Objects/intobject.c
parentcccfc825e49760d8e46db38df50fb992a184b3ee (diff)
downloadcpython-6736cf8d20b67b74e8e959622132963285156242.zip
cpython-6736cf8d20b67b74e8e959622132963285156242.tar.gz
cpython-6736cf8d20b67b74e8e959622132963285156242.tar.bz2
Issue #3166: Make long -> float (and int -> float) conversions
correctly rounded, using round-half-to-even. This ensures that the value of float(n) doesn't depend on whether we're using 15-bit digits or 30-bit digits for Python longs.
Diffstat (limited to 'Objects/intobject.c')
-rw-r--r--Objects/intobject.c80
1 files changed, 68 insertions, 12 deletions
diff --git a/Objects/intobject.c b/Objects/intobject.c
index d4532f4..1b64fe7 100644
--- a/Objects/intobject.c
+++ b/Objects/intobject.c
@@ -3,6 +3,7 @@
#include "Python.h"
#include <ctype.h>
+#include <float.h>
static PyObject *int_int(PyIntObject *v);
@@ -928,12 +929,78 @@ int_long(PyIntObject *v)
return PyLong_FromLong((v -> ob_ival));
}
+static const unsigned char BitLengthTable[32] = {
+ 0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5
+};
+
+static int
+bits_in_ulong(unsigned long d)
+{
+ int d_bits = 0;
+ while (d >= 32) {
+ d_bits += 6;
+ d >>= 6;
+ }
+ d_bits += (int)BitLengthTable[d];
+ return d_bits;
+}
+
+#if 8*SIZEOF_LONG-1 <= DBL_MANT_DIG
+/* Every Python int can be exactly represented as a float. */
+
static PyObject *
int_float(PyIntObject *v)
{
return PyFloat_FromDouble((double)(v -> ob_ival));
}
+#else
+/* Here not all Python ints are exactly representable as floats, so we may
+ have to round. We do this manually, since the C standards don't specify
+ whether converting an integer to a float rounds up or down */
+
+static PyObject *
+int_float(PyIntObject *v)
+{
+ unsigned long abs_ival, lsb;
+ int round_up;
+
+ if (v->ob_ival < 0)
+ abs_ival = 0U-(unsigned long)v->ob_ival;
+ else
+ abs_ival = (unsigned long)v->ob_ival;
+ if (abs_ival < (1L << DBL_MANT_DIG))
+ /* small integer; no need to round */
+ return PyFloat_FromDouble((double)v->ob_ival);
+
+ /* Round abs_ival to MANT_DIG significant bits, using the
+ round-half-to-even rule. abs_ival & lsb picks out the 'rounding'
+ bit: the first bit after the most significant MANT_DIG bits of
+ abs_ival. We round up if this bit is set, provided that either:
+
+ (1) abs_ival isn't exactly halfway between two floats, in which
+ case at least one of the bits following the rounding bit must be
+ set; i.e., abs_ival & lsb-1 != 0, or:
+
+ (2) the resulting rounded value has least significant bit 0; or
+ in other words the bit above the rounding bit is set (this is the
+ 'to-even' bit of round-half-to-even); i.e., abs_ival & 2*lsb != 0
+
+ The condition "(1) or (2)" equates to abs_ival & 3*lsb-1 != 0. */
+
+ lsb = 1L << (bits_in_ulong(abs_ival)-DBL_MANT_DIG-1);
+ round_up = (abs_ival & lsb) && (abs_ival & (3*lsb-1));
+ abs_ival &= -2*lsb;
+ if (round_up)
+ abs_ival += 2*lsb;
+ return PyFloat_FromDouble(v->ob_ival < 0 ?
+ -(double)abs_ival :
+ (double)abs_ival);
+}
+
+#endif
+
static PyObject *
int_oct(PyIntObject *v)
{
@@ -1139,16 +1206,10 @@ int__format__(PyObject *self, PyObject *args)
return NULL;
}
-static const unsigned char BitLengthTable[32] = {
- 0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4,
- 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5
-};
-
static PyObject *
int_bit_length(PyIntObject *v)
{
unsigned long n;
- long r = 0;
if (v->ob_ival < 0)
/* avoid undefined behaviour when v->ob_ival == -LONG_MAX-1 */
@@ -1156,12 +1217,7 @@ int_bit_length(PyIntObject *v)
else
n = (unsigned long)v->ob_ival;
- while (n >= 32) {
- r += 6;
- n >>= 6;
- }
- r += (long)(BitLengthTable[n]);
- return PyInt_FromLong(r);
+ return PyInt_FromLong(bits_in_ulong(n));
}
PyDoc_STRVAR(int_bit_length_doc,