summaryrefslogtreecommitdiffstats
path: root/Objects
diff options
context:
space:
mode:
authorSergey Fedoseev <fedoseev.sergey@gmail.com>2020-05-10 09:15:57 (GMT)
committerGitHub <noreply@github.com>2020-05-10 09:15:57 (GMT)
commit86a93fddf72a2711aca99afa0c5374c8d6b4a321 (patch)
treeed7b1ce67e3c6b2abfcce7d28d06dfb67f78f7b6 /Objects
parent1c2fa781560608aa4be50c748d4b3f403cfa5035 (diff)
downloadcpython-86a93fddf72a2711aca99afa0c5374c8d6b4a321.zip
cpython-86a93fddf72a2711aca99afa0c5374c8d6b4a321.tar.gz
cpython-86a93fddf72a2711aca99afa0c5374c8d6b4a321.tar.bz2
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>
Diffstat (limited to 'Objects')
-rw-r--r--Objects/floatobject.c22
-rw-r--r--Objects/longobject.c18
2 files changed, 17 insertions, 23 deletions
diff --git a/Objects/floatobject.c b/Objects/floatobject.c
index faa02f2..9f50140 100644
--- a/Objects/floatobject.c
+++ b/Objects/floatobject.c
@@ -862,27 +862,7 @@ static PyObject *
float___trunc___impl(PyObject *self)
/*[clinic end generated code: output=dd3e289dd4c6b538 input=591b9ba0d650fdff]*/
{
- double x = PyFloat_AsDouble(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);
+ return PyLong_FromDouble(PyFloat_AS_DOUBLE(self));
}
/*[clinic input]
diff --git a/Objects/longobject.c b/Objects/longobject.c
index 11fc75b..0ff0e80 100644
--- a/Objects/longobject.c
+++ b/Objects/longobject.c
@@ -416,6 +416,21 @@ PyLong_FromSize_t(size_t ival)
PyObject *
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;
double frac;
int i, ndig, expo, neg;
@@ -435,8 +450,7 @@ PyLong_FromDouble(double dval)
dval = -dval;
}
frac = frexp(dval, &expo); /* dval = frac*2**expo; 0.0 <= frac < 1.0 */
- if (expo <= 0)
- return PyLong_FromLong(0L);
+ assert(expo > 0);
ndig = (expo-1) / PyLong_SHIFT + 1; /* Number of 'digits' in result */
v = _PyLong_New(ndig);
if (v == NULL)