mirror of
https://github.com/python/cpython.git
synced 2025-09-26 18:29:57 +00:00
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.
This commit is contained in:
parent
1832de4bc0
commit
9fffa3eea3
5 changed files with 76 additions and 23 deletions
|
@ -328,6 +328,42 @@ def test_auto_overflow():
|
||||||
raise TestFailed("pow%r should have raised "
|
raise TestFailed("pow%r should have raised "
|
||||||
"TypeError" % ((longx, longy, long(z))))
|
"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
|
# ---------------------------------------------------------------- do it
|
||||||
|
|
||||||
test_division()
|
test_division()
|
||||||
|
@ -335,3 +371,4 @@ test_bitop_identities()
|
||||||
test_format()
|
test_format()
|
||||||
test_misc()
|
test_misc()
|
||||||
test_auto_overflow()
|
test_auto_overflow()
|
||||||
|
test_float_overflow()
|
||||||
|
|
12
Misc/NEWS
12
Misc/NEWS
|
@ -3,6 +3,9 @@ What's New in Python 2.2a3?
|
||||||
|
|
||||||
Core
|
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
|
- 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
|
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
|
integer types and the second argument is negative (in which latter case
|
||||||
|
@ -95,6 +98,15 @@ Build
|
||||||
|
|
||||||
API
|
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
|
- 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
|
compile but will not participate in GC. To upgrade an extension
|
||||||
module:
|
module:
|
||||||
|
|
|
@ -522,6 +522,8 @@ complex_coerce(PyObject **pv, PyObject **pw)
|
||||||
}
|
}
|
||||||
else if (PyLong_Check(*pw)) {
|
else if (PyLong_Check(*pw)) {
|
||||||
cval.real = PyLong_AsDouble(*pw);
|
cval.real = PyLong_AsDouble(*pw);
|
||||||
|
if (cval.real == -1.0 && PyErr_Occurred())
|
||||||
|
return -1;
|
||||||
*pw = PyComplex_FromCComplex(cval);
|
*pw = PyComplex_FromCComplex(cval);
|
||||||
Py_INCREF(*pv);
|
Py_INCREF(*pv);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -271,8 +271,7 @@ PyFloat_AsStringEx(char *buf, PyFloatObject *v, int precision)
|
||||||
return obj;
|
return obj;
|
||||||
|
|
||||||
static int
|
static int
|
||||||
convert_to_double(PyObject **v,
|
convert_to_double(PyObject **v, double *dbl)
|
||||||
double *dbl)
|
|
||||||
{
|
{
|
||||||
register PyObject *obj = *v;
|
register PyObject *obj = *v;
|
||||||
|
|
||||||
|
@ -280,9 +279,11 @@ convert_to_double(PyObject **v,
|
||||||
*dbl = (double)PyInt_AS_LONG(obj);
|
*dbl = (double)PyInt_AS_LONG(obj);
|
||||||
}
|
}
|
||||||
else if (PyLong_Check(obj)) {
|
else if (PyLong_Check(obj)) {
|
||||||
PyFPE_START_PROTECT("convert_to_double", {*v=NULL;return -1;})
|
|
||||||
*dbl = PyLong_AsDouble(obj);
|
*dbl = PyLong_AsDouble(obj);
|
||||||
PyFPE_END_PROTECT(*dbl)
|
if (*dbl == -1.0 && PyErr_Occurred()) {
|
||||||
|
*v = NULL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Py_INCREF(Py_NotImplemented);
|
Py_INCREF(Py_NotImplemented);
|
||||||
|
|
|
@ -531,27 +531,28 @@ _PyLong_AsScaledDouble(PyObject *vv, int *exponent)
|
||||||
double
|
double
|
||||||
PyLong_AsDouble(PyObject *vv)
|
PyLong_AsDouble(PyObject *vv)
|
||||||
{
|
{
|
||||||
register PyLongObject *v;
|
int e;
|
||||||
double x;
|
double x;
|
||||||
double multiplier = (double) (1L << SHIFT);
|
|
||||||
int i, sign;
|
|
||||||
|
|
||||||
if (vv == NULL || !PyLong_Check(vv)) {
|
if (vv == NULL || !PyLong_Check(vv)) {
|
||||||
PyErr_BadInternalCall();
|
PyErr_BadInternalCall();
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
v = (PyLongObject *)vv;
|
x = _PyLong_AsScaledDouble(vv, &e);
|
||||||
i = v->ob_size;
|
if (x == -1.0 && PyErr_Occurred())
|
||||||
sign = 1;
|
return -1.0;
|
||||||
x = 0.0;
|
if (e > INT_MAX / SHIFT)
|
||||||
if (i < 0) {
|
goto overflow;
|
||||||
sign = -1;
|
errno = 0;
|
||||||
i = -(i);
|
x = ldexp(x, e * SHIFT);
|
||||||
}
|
if (errno == ERANGE)
|
||||||
while (--i >= 0) {
|
goto overflow;
|
||||||
x = x*multiplier + (double)v->ob_digit[i];
|
return x;
|
||||||
}
|
|
||||||
return x * sign;
|
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 */
|
/* Create a new long (or int) object from a C pointer */
|
||||||
|
@ -2098,9 +2099,9 @@ static PyObject *
|
||||||
long_float(PyObject *v)
|
long_float(PyObject *v)
|
||||||
{
|
{
|
||||||
double result;
|
double result;
|
||||||
PyFPE_START_PROTECT("long_float", return 0)
|
|
||||||
result = PyLong_AsDouble(v);
|
result = PyLong_AsDouble(v);
|
||||||
PyFPE_END_PROTECT(result)
|
if (result == -1.0 && PyErr_Occurred())
|
||||||
|
return NULL;
|
||||||
return PyFloat_FromDouble(result);
|
return PyFloat_FromDouble(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue