Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 7ce0fa8

Browse files
committed
Issue #12080: Fix a performance issue in Decimal._power_exact that causes some corner-case Decimal.__pow__ calls to take an unreasonably long time.
1 parent 4dfcb1a commit 7ce0fa8

3 files changed

Lines changed: 97 additions & 38 deletions

File tree

Lib/decimal.py

Lines changed: 81 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -2001,23 +2001,22 @@ def _power_exact(self, other, p):
20012001
nonzero. For efficiency, other._exp should not be too large,
20022002
so that 10**abs(other._exp) is a feasible calculation."""
20032003

2004-
# In the comments below, we write x for the value of self and
2005-
# y for the value of other. Write x = xc*10**xe and y =
2006-
# yc*10**ye.
2004+
# In the comments below, we write x for the value of self and y for the
2005+
# value of other. Write x = xc*10**xe and abs(y) = yc*10**ye, with xc
2006+
# and yc positive integers not divisible by 10.
20072007

20082008
# The main purpose of this method is to identify the *failure*
20092009
# of x**y to be exactly representable with as little effort as
20102010
# possible. So we look for cheap and easy tests that
20112011
# eliminate the possibility of x**y being exact. Only if all
20122012
# these tests are passed do we go on to actually compute x**y.
20132013

2014-
# Here's the main idea. First normalize both x and y. We
2015-
# express y as a rational m/n, with m and n relatively prime
2016-
# and n>0. Then for x**y to be exactly representable (at
2017-
# *any* precision), xc must be the nth power of a positive
2018-
# integer and xe must be divisible by n. If m is negative
2019-
# then additionally xc must be a power of either 2 or 5, hence
2020-
# a power of 2**n or 5**n.
2014+
# Here's the main idea. Express y as a rational number m/n, with m and
2015+
# n relatively prime and n>0. Then for x**y to be exactly
2016+
# representable (at *any* precision), xc must be the nth power of a
2017+
# positive integer and xe must be divisible by n. If y is negative
2018+
# then additionally xc must be a power of either 2 or 5, hence a power
2019+
# of 2**n or 5**n.
20212020
#
20222021
# There's a limit to how small |y| can be: if y=m/n as above
20232022
# then:
@@ -2089,21 +2088,43 @@ def _power_exact(self, other, p):
20892088
return None
20902089
# now xc is a power of 2; e is its exponent
20912090
e = _nbits(xc)-1
2092-
# find e*y and xe*y; both must be integers
2093-
if ye >= 0:
2094-
y_as_int = yc*10**ye
2095-
e = e*y_as_int
2096-
xe = xe*y_as_int
2097-
else:
2098-
ten_pow = 10**-ye
2099-
e, remainder = divmod(e*yc, ten_pow)
2100-
if remainder:
2101-
return None
2102-
xe, remainder = divmod(xe*yc, ten_pow)
2103-
if remainder:
2104-
return None
2105-
2106-
if e*65 >= p*93: # 93/65 > log(10)/log(5)
2091+
2092+
# We now have:
2093+
#
2094+
# x = 2**e * 10**xe, e > 0, and y < 0.
2095+
#
2096+
# The exact result is:
2097+
#
2098+
# x**y = 5**(-e*y) * 10**(e*y + xe*y)
2099+
#
2100+
# provided that both e*y and xe*y are integers. Note that if
2101+
# 5**(-e*y) >= 10**p, then the result can't be expressed
2102+
# exactly with p digits of precision.
2103+
#
2104+
# Using the above, we can guard against large values of ye.
2105+
# 93/65 is an upper bound for log(10)/log(5), so if
2106+
#
2107+
# ye >= len(str(93*p//65))
2108+
#
2109+
# then
2110+
#
2111+
# -e*y >= -y >= 10**ye > 93*p/65 > p*log(10)/log(5),
2112+
#
2113+
# so 5**(-e*y) >= 10**p, and the coefficient of the result
2114+
# can't be expressed in p digits.
2115+
2116+
# emax >= largest e such that 5**e < 10**p.
2117+
emax = p*93//65
2118+
if ye >= len(str(emax)):
2119+
return None
2120+
2121+
# Find -e*y and -xe*y; both must be integers
2122+
e = _decimal_lshift_exact(e * yc, ye)
2123+
xe = _decimal_lshift_exact(xe * yc, ye)
2124+
if e is None or xe is None:
2125+
return None
2126+
2127+
if e > emax:
21072128
return None
21082129
xc = 5**e
21092130

@@ -2117,19 +2138,20 @@ def _power_exact(self, other, p):
21172138
while xc % 5 == 0:
21182139
xc //= 5
21192140
e -= 1
2120-
if ye >= 0:
2121-
y_as_integer = yc*10**ye
2122-
e = e*y_as_integer
2123-
xe = xe*y_as_integer
2124-
else:
2125-
ten_pow = 10**-ye
2126-
e, remainder = divmod(e*yc, ten_pow)
2127-
if remainder:
2128-
return None
2129-
xe, remainder = divmod(xe*yc, ten_pow)
2130-
if remainder:
2131-
return None
2132-
if e*3 >= p*10: # 10/3 > log(10)/log(2)
2141+
2142+
# Guard against large values of ye, using the same logic as in
2143+
# the 'xc is a power of 2' branch. 10/3 is an upper bound for
2144+
# log(10)/log(2).
2145+
emax = p*10//3
2146+
if ye >= len(str(emax)):
2147+
return None
2148+
2149+
e = _decimal_lshift_exact(e * yc, ye)
2150+
xe = _decimal_lshift_exact(xe * yc, ye)
2151+
if e is None or xe is None:
2152+
return None
2153+
2154+
if e > emax:
21332155
return None
21342156
xc = 2**e
21352157
else:
@@ -5529,6 +5551,27 @@ def _normalize(op1, op2, prec = 0):
55295551

55305552
_nbits = int.bit_length
55315553

5554+
def _decimal_lshift_exact(n, e):
5555+
""" Given integers n and e, return n * 10**e if it's an integer, else None.
5556+
5557+
The computation is designed to avoid computing large powers of 10
5558+
unnecessarily.
5559+
5560+
>>> _decimal_lshift_exact(3, 4)
5561+
30000
5562+
>>> _decimal_lshift_exact(300, -999999999) # returns None
5563+
5564+
"""
5565+
if n == 0:
5566+
return 0
5567+
elif e >= 0:
5568+
return n * 10**e
5569+
else:
5570+
# val_n = largest power of 10 dividing n.
5571+
str_n = str(abs(n))
5572+
val_n = len(str_n) - len(str_n.rstrip('0'))
5573+
return None if val_n < -e else n // 10**-e
5574+
55325575
def _sqrt_nearest(n, a):
55335576
"""Closest integer to the square root of the positive integer n. a is
55345577
an initial approximation to the square root. Any positive integer

Lib/test/decimaltestdata/extra.decTest

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,12 +222,25 @@ extr1700 power 10 1e-999999999 -> 1.000000000000000 Inexact Rounded
222222
extr1701 power 100.0 -557.71e-742888888 -> 1.000000000000000 Inexact Rounded
223223
extr1702 power 10 1e-100 -> 1.000000000000000 Inexact Rounded
224224

225+
-- Another one (see issue #12080). Thanks again to Stefan Krah.
226+
extr1703 power 4 -1.2e-999999999 -> 1.000000000000000 Inexact Rounded
227+
225228
-- A couple of interesting exact cases for power. Note that the specification
226229
-- requires these to be reported as Inexact.
227230
extr1710 power 1e375 56e-3 -> 1.000000000000000E+21 Inexact Rounded
228231
extr1711 power 10000 0.75 -> 1000.000000000000 Inexact Rounded
229232
extr1712 power 1e-24 0.875 -> 1.000000000000000E-21 Inexact Rounded
230233

234+
-- Some more exact cases, exercising power with negative second argument.
235+
extr1720 power 400 -0.5 -> 0.05000000000000000 Inexact Rounded
236+
extr1721 power 4096 -0.75 -> 0.001953125000000000 Inexact Rounded
237+
extr1722 power 625e4 -0.25 -> 0.02000000000000000 Inexact Rounded
238+
239+
-- Nonexact cases, to exercise some of the early exit conditions from
240+
-- _power_exact.
241+
extr1730 power 2048 -0.75 -> 0.003284751622084822 Inexact Rounded
242+
243+
231244
-- Tests for the is_* boolean operations
232245
precision: 9
233246
maxExponent: 999

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,9 @@ Core and Builtins
184184
Library
185185
-------
186186

187+
- Issue #12080: Fix a Decimal.power() case that took an unreasonably long time
188+
to compute.
189+
187190
- Issue #12221: Remove __version__ attributes from pyexpat, pickle, tarfile,
188191
pydoc, tkinter, and xml.parsers.expat. This were useless version constants
189192
left over from the Mercurial transition

0 commit comments

Comments
 (0)