Raise OverflowError when appropriate on long->float conversion. Most of
the fiddling is simply due to that no caller of PyLong_AsDouble ever
checked for failure (so that's fixing old bugs). PyLong_AsDouble is much
faster for big inputs now too, but that's more of a happy consequence
than a design goal.
diff --git a/Lib/test/test_long.py b/Lib/test/test_long.py
index b0eaff7..ac345a6 100644
--- a/Lib/test/test_long.py
+++ b/Lib/test/test_long.py
@@ -328,6 +328,42 @@
raise TestFailed("pow%r should have raised "
"TypeError" % ((longx, longy, long(z))))
+# ---------------------------------------- tests of long->float overflow
+
+def test_float_overflow():
+ import math
+
+ if verbose:
+ print "long->float overflow"
+
+ for x in -2.0, -1.0, 0.0, 1.0, 2.0:
+ verify(float(long(x)) == x)
+
+ huge = 1L << 30000
+ mhuge = -huge
+ namespace = {'huge': huge, 'mhuge': mhuge, 'math': math}
+ for test in ["float(huge)", "float(mhuge)",
+ "complex(huge)", "complex(mhuge)",
+ "complex(huge, 1)", "complex(mhuge, 1)",
+ "complex(1, huge)", "complex(1, mhuge)",
+ "1. + huge", "huge + 1.", "1. + mhuge", "mhuge + 1.",
+ "1. - huge", "huge - 1.", "1. - mhuge", "mhuge - 1.",
+ "1. * huge", "huge * 1.", "1. * mhuge", "mhuge * 1.",
+ "1. // huge", "huge // 1.", "1. // mhuge", "mhuge // 1.",
+ "1. / huge", "huge / 1.", "1. / mhuge", "mhuge / 1.",
+ "1. ** huge", "huge ** 1.", "1. ** mhuge", "mhuge ** 1.",
+ "math.sin(huge)", "math.sin(mhuge)",
+ "math.log(huge)", "math.log(mhuge)", # should do better
+ "math.sqrt(huge)", "math.sqrt(mhuge)", # should do better
+ "math.log10(huge)", "math.log10(mhuge)", # should do better
+ "math.floor(huge)", "math.floor(mhuge)"]:
+
+ try:
+ eval(test, namespace)
+ except OverflowError:
+ pass
+ else:
+ raise TestFailed("expected OverflowError from %s" % test)
# ---------------------------------------------------------------- do it
test_division()
@@ -335,3 +371,4 @@
test_format()
test_misc()
test_auto_overflow()
+test_float_overflow()
diff --git a/Misc/NEWS b/Misc/NEWS
index 43eff32..a8f05c7 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -3,6 +3,9 @@
Core
+- Conversion of long to float now raises OverflowError if the long is too
+ big to represent as a C double.
+
- The 3-argument builtin pow() no longer allows a third non-None argument
if either of the first two arguments is a float, or if both are of
integer types and the second argument is negative (in which latter case
@@ -95,6 +98,15 @@
API
+- Note that PyLong_AsDouble can fail! This has always been true, but no
+ callers checked for it. It's more likely to fail now, because overflow
+ errors are properly detected now. The proper way to check:
+
+ double x = PyLong_AsDouble(some_long_object);
+ if (x == -1.0 && PyErr_Occurred()) {
+ /* The conversion failed. */
+ }
+
- The GC API has been changed. Extensions that use the old API will still
compile but will not participate in GC. To upgrade an extension
module:
diff --git a/Objects/complexobject.c b/Objects/complexobject.c
index 236f4d5..7404993 100644
--- a/Objects/complexobject.c
+++ b/Objects/complexobject.c
@@ -522,6 +522,8 @@
}
else if (PyLong_Check(*pw)) {
cval.real = PyLong_AsDouble(*pw);
+ if (cval.real == -1.0 && PyErr_Occurred())
+ return -1;
*pw = PyComplex_FromCComplex(cval);
Py_INCREF(*pv);
return 0;
diff --git a/Objects/floatobject.c b/Objects/floatobject.c
index 478e131..8443aff 100644
--- a/Objects/floatobject.c
+++ b/Objects/floatobject.c
@@ -271,18 +271,19 @@
return obj;
static int
-convert_to_double(PyObject **v,
- double *dbl)
+convert_to_double(PyObject **v, double *dbl)
{
register PyObject *obj = *v;
-
+
if (PyInt_Check(obj)) {
*dbl = (double)PyInt_AS_LONG(obj);
}
else if (PyLong_Check(obj)) {
- PyFPE_START_PROTECT("convert_to_double", {*v=NULL;return -1;})
*dbl = PyLong_AsDouble(obj);
- PyFPE_END_PROTECT(*dbl)
+ if (*dbl == -1.0 && PyErr_Occurred()) {
+ *v = NULL;
+ return -1;
+ }
}
else {
Py_INCREF(Py_NotImplemented);
diff --git a/Objects/longobject.c b/Objects/longobject.c
index b511928..e97ebd5 100644
--- a/Objects/longobject.c
+++ b/Objects/longobject.c
@@ -531,27 +531,28 @@
double
PyLong_AsDouble(PyObject *vv)
{
- register PyLongObject *v;
+ int e;
double x;
- double multiplier = (double) (1L << SHIFT);
- int i, sign;
-
+
if (vv == NULL || !PyLong_Check(vv)) {
PyErr_BadInternalCall();
return -1;
}
- v = (PyLongObject *)vv;
- i = v->ob_size;
- sign = 1;
- x = 0.0;
- if (i < 0) {
- sign = -1;
- i = -(i);
- }
- while (--i >= 0) {
- x = x*multiplier + (double)v->ob_digit[i];
- }
- return x * sign;
+ x = _PyLong_AsScaledDouble(vv, &e);
+ if (x == -1.0 && PyErr_Occurred())
+ return -1.0;
+ if (e > INT_MAX / SHIFT)
+ goto overflow;
+ errno = 0;
+ x = ldexp(x, e * SHIFT);
+ if (errno == ERANGE)
+ goto overflow;
+ return x;
+
+overflow:
+ PyErr_SetString(PyExc_OverflowError,
+ "long int too large to convert to float");
+ return -1.0;
}
/* Create a new long (or int) object from a C pointer */
@@ -2098,9 +2099,9 @@
long_float(PyObject *v)
{
double result;
- PyFPE_START_PROTECT("long_float", return 0)
result = PyLong_AsDouble(v);
- PyFPE_END_PROTECT(result)
+ if (result == -1.0 && PyErr_Occurred())
+ return NULL;
return PyFloat_FromDouble(result);
}