-
-
Notifications
You must be signed in to change notification settings - Fork 33.7k
bpo-37986: Improve perfomance of PyLong_FromDouble() #15611
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are these numbers still correct? There are other changes related to trunc/floor/ceil in 3.9, they can reduce or increase the relative effect of this optimization.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we could just drop the second sentence and keep the first here.