mirror of https://github.com/python/cpython
bpo-37986: Improve perfomance of PyLong_FromDouble() (GH-15611)
* bpo-37986: Improve perfomance of PyLong_FromDouble() * Use strict bound check for safety and symmetry * Remove possibly outdated performance claims Co-authored-by: Mark Dickinson <dickinsm@gmail.com>
This commit is contained in:
parent
1c2fa78156
commit
86a93fddf7
|
@ -0,0 +1,2 @@
|
||||||
|
Improve performance of :c:func:`PyLong_FromDouble` for values that fit into
|
||||||
|
:c:type:`long`.
|
|
@ -862,27 +862,7 @@ static PyObject *
|
||||||
float___trunc___impl(PyObject *self)
|
float___trunc___impl(PyObject *self)
|
||||||
/*[clinic end generated code: output=dd3e289dd4c6b538 input=591b9ba0d650fdff]*/
|
/*[clinic end generated code: output=dd3e289dd4c6b538 input=591b9ba0d650fdff]*/
|
||||||
{
|
{
|
||||||
double x = PyFloat_AsDouble(self);
|
return PyLong_FromDouble(PyFloat_AS_DOUBLE(self));
|
||||||
double wholepart; /* integral portion of x, rounded toward 0 */
|
|
||||||
|
|
||||||
(void)modf(x, &wholepart);
|
|
||||||
/* Try to get out cheap if this fits in a Python int. The attempt
|
|
||||||
* to cast to long must be protected, as C doesn't define what
|
|
||||||
* happens if the double is too big to fit in a long. Some rare
|
|
||||||
* systems raise an exception then (RISCOS was mentioned as one,
|
|
||||||
* and someone using a non-default option on Sun also bumped into
|
|
||||||
* that). Note that checking for >= and <= LONG_{MIN,MAX} would
|
|
||||||
* still be vulnerable: if a long has more bits of precision than
|
|
||||||
* a double, casting MIN/MAX to double may yield an approximation,
|
|
||||||
* and if that's rounded up, then, e.g., wholepart=LONG_MAX+1 would
|
|
||||||
* yield true from the C expression wholepart<=LONG_MAX, despite
|
|
||||||
* that wholepart is actually greater than LONG_MAX.
|
|
||||||
*/
|
|
||||||
if (LONG_MIN < wholepart && wholepart < LONG_MAX) {
|
|
||||||
const long aslong = (long)wholepart;
|
|
||||||
return PyLong_FromLong(aslong);
|
|
||||||
}
|
|
||||||
return PyLong_FromDouble(wholepart);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*[clinic input]
|
/*[clinic input]
|
||||||
|
|
|
@ -416,6 +416,21 @@ PyLong_FromSize_t(size_t ival)
|
||||||
PyObject *
|
PyObject *
|
||||||
PyLong_FromDouble(double dval)
|
PyLong_FromDouble(double dval)
|
||||||
{
|
{
|
||||||
|
/* Try to get out cheap if this fits in a long. When a finite value of real
|
||||||
|
* floating type is converted to an integer type, the value is truncated
|
||||||
|
* toward zero. If the value of the integral part cannot be represented by
|
||||||
|
* the integer type, the behavior is undefined. Thus, we must check that
|
||||||
|
* value is in range (LONG_MIN - 1, LONG_MAX + 1). If a long has more bits
|
||||||
|
* of precision than a double, casting LONG_MIN - 1 to double may yield an
|
||||||
|
* approximation, but LONG_MAX + 1 is a power of two and can be represented
|
||||||
|
* as double exactly (assuming FLT_RADIX is 2 or 16), so for simplicity
|
||||||
|
* check against [-(LONG_MAX + 1), LONG_MAX + 1).
|
||||||
|
*/
|
||||||
|
const double int_max = (unsigned long)LONG_MAX + 1;
|
||||||
|
if (-int_max < dval && dval < int_max) {
|
||||||
|
return PyLong_FromLong((long)dval);
|
||||||
|
}
|
||||||
|
|
||||||
PyLongObject *v;
|
PyLongObject *v;
|
||||||
double frac;
|
double frac;
|
||||||
int i, ndig, expo, neg;
|
int i, ndig, expo, neg;
|
||||||
|
@ -435,8 +450,7 @@ PyLong_FromDouble(double dval)
|
||||||
dval = -dval;
|
dval = -dval;
|
||||||
}
|
}
|
||||||
frac = frexp(dval, &expo); /* dval = frac*2**expo; 0.0 <= frac < 1.0 */
|
frac = frexp(dval, &expo); /* dval = frac*2**expo; 0.0 <= frac < 1.0 */
|
||||||
if (expo <= 0)
|
assert(expo > 0);
|
||||||
return PyLong_FromLong(0L);
|
|
||||||
ndig = (expo-1) / PyLong_SHIFT + 1; /* Number of 'digits' in result */
|
ndig = (expo-1) / PyLong_SHIFT + 1; /* Number of 'digits' in result */
|
||||||
v = _PyLong_New(ndig);
|
v = _PyLong_New(ndig);
|
||||||
if (v == NULL)
|
if (v == NULL)
|
||||||
|
|
Loading…
Reference in New Issue