From 0390f504ac81c9eb2b71eea81204284f01b200a2 Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Thu, 8 Jul 2010 19:21:59 +0000 Subject: [PATCH] Merged revisions 82646,82649-82650 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k ........ r82646 | mark.dickinson | 2010-07-08 18:23:40 +0100 (Thu, 08 Jul 2010) | 1 line In test_decimal, convert heuristic for skipping tests into an explicit skiplist. ........ r82649 | mark.dickinson | 2010-07-08 20:03:34 +0100 (Thu, 08 Jul 2010) | 1 line Fix a performance issue in Decimal.pow. Thanks Stefan Krah for finding this. ........ r82650 | mark.dickinson | 2010-07-08 20:09:16 +0100 (Thu, 08 Jul 2010) | 1 line Fix misplaced exactness check that was causing unnecessary work in Decimal.__pow__. ........ --- Lib/decimal.py | 21 ++++--- Lib/test/decimaltestdata/extra.decTest | 13 +++++ Lib/test/test_decimal.py | 76 ++++++++++++-------------- Misc/NEWS | 4 ++ 4 files changed, 64 insertions(+), 50 deletions(-) diff --git a/Lib/decimal.py b/Lib/decimal.py index ecf0cd523e..958b2f9a72 100644 --- a/Lib/decimal.py +++ b/Lib/decimal.py @@ -2044,12 +2044,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 @@ -2322,9 +2324,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 2640842c68..fce8435599 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 dad63aee31..e9fbad44ca 100644 --- a/Lib/test/test_decimal.py +++ b/Lib/test/test_decimal.py @@ -71,10 +71,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. @@ -165,27 +196,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. @@ -323,22 +333,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 87af41cdda..a236f60b27 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -75,6 +75,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. -- 2.40.0