From e85aa739ab0d396665908c0a489cfdeb49c88674 Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Thu, 8 Jul 2010 19:24:40 +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 a8e4f19999b..71aaa5cf516 100644 --- a/Lib/decimal.py +++ b/Lib/decimal.py @@ -1990,12 +1990,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 @@ -2268,9 +2270,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 2640842c68e..fce84355999 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 c9a096a86de..f8d8dec5ade 100644 --- a/Lib/test/test_decimal.py +++ b/Lib/test/test_decimal.py @@ -77,10 +77,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. @@ -171,27 +202,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. @@ -329,22 +339,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 3e615b9bb47..d895d0495df 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -515,6 +515,10 @@ Core and Builtins - Issue #8329: Don't return the same lists from select.select when no fds are changed. +- 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 #8259: ``1L << (2**31)`` no longer produces an 'outrageous shift error' on 64-bit machines. The shift count for either left or right shift is permitted to be up to sys.maxsize.