From 5dab8e2fcabfa6cd37d313376878ca2b87519eed Mon Sep 17 00:00:00 2001 From: Sergey Fedoseev Date: Thu, 29 Aug 2019 18:39:50 +0500 Subject: [PATCH 1/3] bpo-37986: Improve perfomance of PyLong_FromDouble() --- .../2019-11-20-09-50-58.bpo-37986.o0lmA7.rst | 4 ++++ Objects/floatobject.c | 22 +------------------ Objects/longobject.c | 18 +++++++++++++-- 3 files changed, 21 insertions(+), 23 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2019-11-20-09-50-58.bpo-37986.o0lmA7.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-11-20-09-50-58.bpo-37986.o0lmA7.rst b/Misc/NEWS.d/next/Core and Builtins/2019-11-20-09-50-58.bpo-37986.o0lmA7.rst new file mode 100644 index 00000000000000..4bc97168bbef8c --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2019-11-20-09-50-58.bpo-37986.o0lmA7.rst @@ -0,0 +1,4 @@ +Improve performance of :c:func:`PyLong_FromDouble` for values that fit into +:c:type:`long`. Now :meth:`float.__trunc__` is faster up to 10%, +:func:`math.floor()` and :func:`math.ceil()` are faster up to 30% when used +with such values. diff --git a/Objects/floatobject.c b/Objects/floatobject.c index 20a5155e07346d..e259dd61981ee5 100644 --- a/Objects/floatobject.c +++ b/Objects/floatobject.c @@ -882,27 +882,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)); } /* double_round: rounds a finite double to the closest multiple of diff --git a/Objects/longobject.c b/Objects/longobject.c index 2b57ea18666eec..a1e6bd0f8f8814 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -433,6 +433,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; @@ -452,8 +467,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) From 0b90ead7d23be2fced6b547f4d6426a902e04a7f Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Sun, 10 May 2020 09:46:07 +0100 Subject: [PATCH 2/3] Use strict bound check for safety and symmetry --- Objects/longobject.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Objects/longobject.c b/Objects/longobject.c index a1e6bd0f8f8814..ea08b8fbe97517 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -444,7 +444,7 @@ PyLong_FromDouble(double dval) * check against [-(LONG_MAX + 1), LONG_MAX + 1). */ const double int_max = (unsigned long)LONG_MAX + 1; - if (-int_max <= dval && dval < int_max) { + if (-int_max < dval && dval < int_max) { return PyLong_FromLong((long)dval); } From 8be513e45e68bd525aec8a3f49040102d0d74ed1 Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Sun, 10 May 2020 09:51:25 +0100 Subject: [PATCH 3/3] Remove possibly outdated performance claims --- .../2019-11-20-09-50-58.bpo-37986.o0lmA7.rst | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-11-20-09-50-58.bpo-37986.o0lmA7.rst b/Misc/NEWS.d/next/Core and Builtins/2019-11-20-09-50-58.bpo-37986.o0lmA7.rst index 4bc97168bbef8c..62446e35ae01ba 100644 --- a/Misc/NEWS.d/next/Core and Builtins/2019-11-20-09-50-58.bpo-37986.o0lmA7.rst +++ b/Misc/NEWS.d/next/Core and Builtins/2019-11-20-09-50-58.bpo-37986.o0lmA7.rst @@ -1,4 +1,2 @@ Improve performance of :c:func:`PyLong_FromDouble` for values that fit into -:c:type:`long`. Now :meth:`float.__trunc__` is faster up to 10%, -:func:`math.floor()` and :func:`math.ceil()` are faster up to 30% when used -with such values. +:c:type:`long`. pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy