summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMark Dickinson <dickinsm@gmail.com>2010-07-08 19:27:24 (GMT)
committerMark Dickinson <dickinsm@gmail.com>2010-07-08 19:27:24 (GMT)
commit060d6556aa7042a1edf0a3c4f7bcff579804be2f (patch)
tree7d2fd61b51c7d395a5e40af22cd6d0ebbfc0fa43
parentcf212d429262899bf8295f55a0b1b855031e20fd (diff)
downloadcpython-060d6556aa7042a1edf0a3c4f7bcff579804be2f.zip
cpython-060d6556aa7042a1edf0a3c4f7bcff579804be2f.tar.gz
cpython-060d6556aa7042a1edf0a3c4f7bcff579804be2f.tar.bz2
Fix Decimal speed issue; backport of r82652 from release27-maint.
-rw-r--r--Lib/decimal.py21
-rw-r--r--Lib/test/decimaltestdata/extra.decTest13
-rw-r--r--Lib/test/test_decimal.py76
-rw-r--r--Misc/NEWS4
4 files changed, 64 insertions, 50 deletions
diff --git a/Lib/decimal.py b/Lib/decimal.py
index b961840..3819759 100644
--- a/Lib/decimal.py
+++ b/Lib/decimal.py
@@ -1918,12 +1918,14 @@ class Decimal(object):
# case where xc == 1: result is 10**(xe*y), with xe*y
# required to be an integer
if xc == 1:
- if ye >= 0:
- exponent = xe*yc*10**ye
- else:
- exponent, remainder = divmod(xe*yc, 10**-ye)
- if remainder:
- return None
+ xe *= yc
+ # result is now 10**(xe * 10**ye); xe * 10**ye must be integral
+ while xe % 10 == 0:
+ xe //= 10
+ ye += 1
+ if ye < 0:
+ return None
+ exponent = xe * 10**ye
if y.sign == 1:
exponent = -exponent
# if other is a nonnegative integer, use ideal exponent
@@ -2196,9 +2198,10 @@ class Decimal(object):
# try for an exact result with precision +1
if ans is None:
ans = self._power_exact(other, context.prec + 1)
- if ans is not None and result_sign == 1:
- ans = _dec_from_triple(1, ans._int, ans._exp)
- exact = True
+ if ans is not None:
+ if result_sign == 1:
+ ans = _dec_from_triple(1, ans._int, ans._exp)
+ exact = True
# usual case: inexact result, x**y computed directly as exp(y*log(x))
if ans is None:
diff --git a/Lib/test/decimaltestdata/extra.decTest b/Lib/test/decimaltestdata/extra.decTest
index 2640842..fce8435 100644
--- a/Lib/test/decimaltestdata/extra.decTest
+++ b/Lib/test/decimaltestdata/extra.decTest
@@ -213,7 +213,20 @@ extr1658 shift 1234567 3 -> 7000
extr1659 shift 1234567 4 -> 0
extr1660 shift 1234567 5 -> NaN Invalid_operation
+-- Cases where the power function was impossibly slow to determine that the
+-- result is inexact. Thanks Stefan Krah for identifying this problem.
+precision: 16
+maxExponent: 999999999
+minExponent: -999999999
+extr1700 power 10 1e-999999999 -> 1.000000000000000 Inexact Rounded
+extr1701 power 100.0 -557.71e-742888888 -> 1.000000000000000 Inexact Rounded
+extr1702 power 10 1e-100 -> 1.000000000000000 Inexact Rounded
+-- A couple of interesting exact cases for power. Note that the specification
+-- requires these to be reported as Inexact.
+extr1710 power 1e375 56e-3 -> 1.000000000000000E+21 Inexact Rounded
+extr1711 power 10000 0.75 -> 1000.000000000000 Inexact Rounded
+extr1712 power 1e-24 0.875 -> 1.000000000000000E-21 Inexact Rounded
-- Tests for the is_* boolean operations
precision: 9
diff --git a/Lib/test/test_decimal.py b/Lib/test/test_decimal.py
index 9b5fe15..cc15c33 100644
--- a/Lib/test/test_decimal.py
+++ b/Lib/test/test_decimal.py
@@ -72,10 +72,41 @@ skip_expected = not os.path.isdir(directory)
# list of individual .decTest test ids that correspond to tests that
# we're skipping for one reason or another.
-skipped_test_ids = [
- 'scbx164', # skipping apparently implementation-specific scaleb
- 'scbx165', # tests, pending clarification of scaleb rules.
-]
+skipped_test_ids = set([
+ # Skip implementation-specific scaleb tests.
+ 'scbx164',
+ 'scbx165',
+
+ # For some operations (currently exp, ln, log10, power), the decNumber
+ # reference implementation imposes additional restrictions on the context
+ # and operands. These restrictions are not part of the specification;
+ # however, the effect of these restrictions does show up in some of the
+ # testcases. We skip testcases that violate these restrictions, since
+ # Decimal behaves differently from decNumber for these testcases so these
+ # testcases would otherwise fail.
+ 'expx901',
+ 'expx902',
+ 'expx903',
+ 'expx905',
+ 'lnx901',
+ 'lnx902',
+ 'lnx903',
+ 'lnx905',
+ 'logx901',
+ 'logx902',
+ 'logx903',
+ 'logx905',
+ 'powx1183',
+ 'powx1184',
+ 'powx4001',
+ 'powx4002',
+ 'powx4003',
+ 'powx4005',
+ 'powx4008',
+ 'powx4010',
+ 'powx4012',
+ 'powx4014',
+ ])
# Make sure it actually raises errors when not expected and caught in flags
# Slower, since it runs some things several times.
@@ -166,27 +197,6 @@ LOGICAL_FUNCTIONS = (
'same_quantum',
)
-# For some operations (currently exp, ln, log10, power), the decNumber
-# reference implementation imposes additional restrictions on the
-# context and operands. These restrictions are not part of the
-# specification; however, the effect of these restrictions does show
-# up in some of the testcases. We skip testcases that violate these
-# restrictions, since Decimal behaves differently from decNumber for
-# these testcases so these testcases would otherwise fail.
-
-decNumberRestricted = ('power', 'ln', 'log10', 'exp')
-DEC_MAX_MATH = 999999
-def outside_decNumber_bounds(v, context):
- if (context.prec > DEC_MAX_MATH or
- context.Emax > DEC_MAX_MATH or
- -context.Emin > DEC_MAX_MATH):
- return True
- if not v._is_special and v and (
- v.adjusted() > DEC_MAX_MATH or
- v.adjusted() < 1-2*DEC_MAX_MATH):
- return True
- return False
-
class DecimalTest(unittest.TestCase):
"""Class which tests the Decimal class against the test cases.
@@ -324,22 +334,6 @@ class DecimalTest(unittest.TestCase):
ans = FixQuotes(ans)
- # skip tests that are related to bounds imposed in the decNumber
- # reference implementation
- if fname in decNumberRestricted:
- if fname == 'power':
- if not (vals[1]._isinteger() and
- -1999999997 <= vals[1] <= 999999999):
- if outside_decNumber_bounds(vals[0], self.context) or \
- outside_decNumber_bounds(vals[1], self.context):
- #print "Skipping test %s" % s
- return
- else:
- if outside_decNumber_bounds(vals[0], self.context):
- #print "Skipping test %s" % s
- return
-
-
if EXTENDEDERRORTEST and fname not in ('to_sci_string', 'to_eng_string'):
for error in theirexceptions:
self.context.traps[error] = 1
diff --git a/Misc/NEWS b/Misc/NEWS
index d1a5dbe..70f8853 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -81,6 +81,10 @@ C-API
Library
-------
+- Fix extreme speed issue in Decimal.pow when the base is an exact
+ power of 10 and the exponent is tiny (for example,
+ Decimal(10) ** Decimal('1e-999999999')).
+
- Issue #9130: Fix validation of relative imports in parser module.
- Issue #9128: Fix validation of class decorators in parser module.