]> granicus.if.org Git - python/commitdiff
Raise OverflowError when appropriate on long->float conversion. Most of
authorTim Peters <tim.peters@gmail.com>
Tue, 4 Sep 2001 05:14:19 +0000 (05:14 +0000)
committerTim Peters <tim.peters@gmail.com>
Tue, 4 Sep 2001 05:14:19 +0000 (05:14 +0000)
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.

Lib/test/test_long.py
Misc/NEWS
Objects/complexobject.c
Objects/floatobject.c
Objects/longobject.c

index b0eaff7eb77ccf63eeb39825b22389d2ef98eb06..ac345a661f9b104052d98d7b0b6bd9abefc5b42e 100644 (file)
@@ -328,6 +328,42 @@ def test_auto_overflow():
                                 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_bitop_identities()
 test_format()
 test_misc()
 test_auto_overflow()
+test_float_overflow()
index 43eff32c2629de52ca385553f22491b021139d06..a8f05c7e82db3393dc268d061134a455be23f593 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -3,6 +3,9 @@ What's New in Python 2.2a3?
 
 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 @@ Build
 
 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:
index 236f4d52427a18a7821d67a3c9a2f7b45392884c..740499319a9fef3b754eb5332adde7bf63afd9ae 100644 (file)
@@ -522,6 +522,8 @@ complex_coerce(PyObject **pv, PyObject **pw)
        }
        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;
index 478e13174f27caa751540133f46d3b1fd06ed4aa..8443aff4aeaf781e07ee98e0106de5f97ce904bd 100644 (file)
@@ -271,18 +271,19 @@ PyFloat_AsStringEx(char *buf, PyFloatObject *v, int precision)
                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);
index b511928dbd4ba570a1e80fef3ecadf0d88ec7245..e97ebd59888a3bc309df400b3c34c21aaea6c20b 100644 (file)
@@ -531,27 +531,28 @@ _PyLong_AsScaledDouble(PyObject *vv, int *exponent)
 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 @@ static PyObject *
 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);
 }