summaryrefslogtreecommitdiffstats
path: root/Lib/decimal.py
diff options
context:
space:
mode:
authorGuido van Rossum <guido@python.org>2007-11-26 23:23:18 (GMT)
committerGuido van Rossum <guido@python.org>2007-11-26 23:23:18 (GMT)
commit0d3fb8a944a810f421377d5823cbc006700b3c1d (patch)
treeec818742da4c475ef7e05a54f086384d6bbf52d9 /Lib/decimal.py
parent4975a1f3f3aa54d1adcc6669972d9e1563405f23 (diff)
downloadcpython-0d3fb8a944a810f421377d5823cbc006700b3c1d.zip
cpython-0d3fb8a944a810f421377d5823cbc006700b3c1d.tar.gz
cpython-0d3fb8a944a810f421377d5823cbc006700b3c1d.tar.bz2
Merged revisions 59107-59186 via svnmerge from
svn+ssh://pythondev@svn.python.org/python/trunk (Note: some conflicts in the PCbuild9 directory reverted. Sorry Christian!) ........ r59120 | christian.heimes | 2007-11-22 03:21:16 -0800 (Thu, 22 Nov 2007) | 3 lines Backport of the PCbuild9 directory from the py3k branch. I've finished the last task for the PCbuild9 directory today. I don't think there is much left to do. Now you can all play around with the shiny new VS 2008 and try the PGO builds. I was able to get a speed improvement of about 10% on py3k. Have fun! :) ........ r59126 | brett.cannon | 2007-11-22 16:06:51 -0800 (Thu, 22 Nov 2007) | 2 lines Fix a bug in the test for using __loader__.get_data(). ........ r59131 | christian.heimes | 2007-11-22 23:05:03 -0800 (Thu, 22 Nov 2007) | 1 line Backport of PCbuild9 fixes from py3k r59130 ........ r59132 | christian.heimes | 2007-11-23 01:10:36 -0800 (Fri, 23 Nov 2007) | 2 lines Applied patch #1754273 and #1754271 from Thomas Glee The patches are adding deprecation warnings for back ticks and <> ........ r59133 | christian.heimes | 2007-11-23 04:12:02 -0800 (Fri, 23 Nov 2007) | 2 lines Fixed problems in the last commit. Filenames and line numbers weren't reported correctly. Backquotes still don't report the correct file. The AST nodes only contain the line number but not the file name. ........ r59134 | christian.heimes | 2007-11-23 04:16:35 -0800 (Fri, 23 Nov 2007) | 1 line How did the comment get there? ........ r59135 | christian.heimes | 2007-11-23 05:25:31 -0800 (Fri, 23 Nov 2007) | 1 line And yet another fix for the patch. Paul Moore has send me a note that I've missed a declaration. The additional code has moved the declaration in the middle of the block. ........ r59136 | andrew.kuchling | 2007-11-23 05:37:39 -0800 (Fri, 23 Nov 2007) | 1 line Add item ........ r59137 | skip.montanaro | 2007-11-23 09:08:35 -0800 (Fri, 23 Nov 2007) | 2 lines Make trace and doctest play nice together (issue 1429818). Will backport. ........ r59139 | skip.montanaro | 2007-11-23 09:12:47 -0800 (Fri, 23 Nov 2007) | 1 line issue 1429818 ........ r59144 | facundo.batista | 2007-11-23 09:59:00 -0800 (Fri, 23 Nov 2007) | 10 lines Major change in the internal structure of the Decimal number: now it does not store the mantissa as a tuple of numbers, but as a string. This avoids a lot of conversions, and achieves a speedup of 40%. The API remains intact. Thanks Mark Dickinson. ........ r59146 | facundo.batista | 2007-11-23 10:14:50 -0800 (Fri, 23 Nov 2007) | 3 lines Test cases from Cowlishaw, v2.57. All are pased cleanly. ........ r59156 | christian.heimes | 2007-11-23 17:36:02 -0800 (Fri, 23 Nov 2007) | 2 lines Added filename to compiling struct based on Martin's suggestion. I'm wonder why I was trying to add the filename to the node all the time. The compiling struct is more obvious. ........ r59158 | christian.heimes | 2007-11-23 17:53:59 -0800 (Fri, 23 Nov 2007) | 2 lines Backport of fixes from py3k branch svn merge -r59131:HEAD ../../py3k/PCbuild9/ . ........ r59159 | skip.montanaro | 2007-11-23 20:29:08 -0800 (Fri, 23 Nov 2007) | 1 line revert change that breaks test_doctest (which I forgot to run - sorry) ........ r59162 | skip.montanaro | 2007-11-23 20:31:15 -0800 (Fri, 23 Nov 2007) | 1 line revert ........ r59164 | georg.brandl | 2007-11-24 03:31:46 -0800 (Sat, 24 Nov 2007) | 3 lines #1344: document that you need to open std{in,out,err} with PIPE if you want communicate() to work as described. ........ r59165 | georg.brandl | 2007-11-24 03:39:13 -0800 (Sat, 24 Nov 2007) | 2 lines #1467: fix documentation for TestResult.add{Error,Failure}. ........ r59166 | georg.brandl | 2007-11-24 03:42:14 -0800 (Sat, 24 Nov 2007) | 2 lines #1355: remove mention of PyXML from xml.dom docs. ........ r59169 | amaury.forgeotdarc | 2007-11-24 05:20:22 -0800 (Sat, 24 Nov 2007) | 2 lines Warning "<> not supported in 3.x" should be enabled only when the -3 option is set. ........ r59170 | amaury.forgeotdarc | 2007-11-24 05:44:17 -0800 (Sat, 24 Nov 2007) | 3 lines Issue #1445: Fix a SystemError when accessing the ``cell_contents`` attribute of an empty cell object. Now a ValueError is raised. ........ r59172 | georg.brandl | 2007-11-24 05:56:09 -0800 (Sat, 24 Nov 2007) | 3 lines #1735632: add O_NOATIME constant to os module. Also document a few other O_ constants that were missing from documentation. ........ r59173 | skip.montanaro | 2007-11-24 06:30:47 -0800 (Sat, 24 Nov 2007) | 1 line back in these go - thanks to Titus Brown for the fix ........ r59176 | martin.v.loewis | 2007-11-24 10:33:40 -0800 (Sat, 24 Nov 2007) | 2 lines Bug #1494: Document that appendChild removes first. ........ r59186 | guido.van.rossum | 2007-11-26 14:16:49 -0800 (Mon, 26 Nov 2007) | 2 lines A thread-less variant of brownian.py, submitted by Michele Simoniato. ........
Diffstat (limited to 'Lib/decimal.py')
-rw-r--r--Lib/decimal.py525
1 files changed, 219 insertions, 306 deletions
diff --git a/Lib/decimal.py b/Lib/decimal.py
index faf9bf7..7842cb2 100644
--- a/Lib/decimal.py
+++ b/Lib/decimal.py
@@ -214,10 +214,10 @@ class InvalidOperation(DecimalException):
def handle(self, context, *args):
if args:
if args[0] == 1: # sNaN, must drop 's' but keep diagnostics
- ans = Decimal((args[1]._sign, args[1]._int, 'n'))
+ ans = _dec_from_triple(args[1]._sign, args[1]._int, 'n', True)
return ans._fix_nan(context)
elif args[0] == 2:
- return Decimal( (args[1], args[2], 'n') )
+ return _dec_from_triple(args[1], args[2], 'n', True)
return NaN
@@ -350,13 +350,13 @@ class Overflow(Inexact, Rounded):
if sign == 0:
if context.rounding == ROUND_CEILING:
return Infsign[sign]
- return Decimal((sign, (9,)*context.prec,
- context.Emax-context.prec+1))
+ return _dec_from_triple(sign, '9'*context.prec,
+ context.Emax-context.prec+1)
if sign == 1:
if context.rounding == ROUND_FLOOR:
return Infsign[sign]
- return Decimal( (sign, (9,)*context.prec,
- context.Emax-context.prec+1))
+ return _dec_from_triple(sign, '9'*context.prec,
+ context.Emax-context.prec+1)
class Underflow(Inexact, Rounded, Subnormal):
@@ -531,13 +531,21 @@ class Decimal(object):
Decimal("314")
"""
+ # Note that the coefficient, self._int, is actually stored as
+ # a string rather than as a tuple of digits. This speeds up
+ # the "digits to integer" and "integer to digits" conversions
+ # that are used in almost every arithmetic operation on
+ # Decimals. This is an internal detail: the as_tuple function
+ # and the Decimal constructor still deal with tuples of
+ # digits.
+
self = object.__new__(cls)
self._is_special = False
# From an internal working value
if isinstance(value, _WorkRep):
self._sign = value.sign
- self._int = tuple(map(int, str(value.int)))
+ self._int = str(value.int)
self._exp = int(value.exp)
return self
@@ -556,7 +564,7 @@ class Decimal(object):
else:
self._sign = 1
self._exp = 0
- self._int = tuple(map(int, str(abs(value))))
+ self._int = str(abs(value))
return self
# tuple/list conversion (possibly from as_tuple())
@@ -573,7 +581,7 @@ class Decimal(object):
self._sign = value[0]
if value[2] == 'F':
# infinity: value[1] is ignored
- self._int = (0,)
+ self._int = '0'
self._exp = value[2]
self._is_special = True
else:
@@ -590,12 +598,12 @@ class Decimal(object):
"0 through 9.")
if value[2] in ('n', 'N'):
# NaN: digits form the diagnostic
- self._int = tuple(digits)
+ self._int = ''.join(map(str, digits))
self._exp = value[2]
self._is_special = True
elif isinstance(value[2], int):
# finite number: digits give the coefficient
- self._int = tuple(digits or [0])
+ self._int = ''.join(map(str, digits or [0]))
self._exp = value[2]
self._is_special = False
else:
@@ -608,38 +616,46 @@ class Decimal(object):
raise TypeError("Cannot convert float to Decimal. " +
"First convert the float to a string")
- # Other argument types may require the context during interpretation
- if context is None:
- context = getcontext()
-
# From a string
# REs insist on real strings, so we can too.
if isinstance(value, str):
- if _isinfinity(value):
- self._exp = 'F'
- self._int = (0,)
- self._is_special = True
- if _isinfinity(value) == 1:
- self._sign = 0
+ m = _parser(value)
+ if m is None:
+ if context is None:
+ context = getcontext()
+ return context._raise_error(ConversionSyntax,
+ "Invalid literal for Decimal: %r" % value)
+
+ if m.group('sign') == "-":
+ self._sign = 1
+ else:
+ self._sign = 0
+ intpart = m.group('int')
+ if intpart is not None:
+ # finite number
+ fracpart = m.group('frac')
+ exp = int(m.group('exp') or '0')
+ if fracpart is not None:
+ self._int = (intpart+fracpart).lstrip('0') or '0'
+ self._exp = exp - len(fracpart)
else:
- self._sign = 1
- return self
- if _isnan(value):
- sig, sign, diag = _isnan(value)
- self._is_special = True
- if sig == 1:
- self._exp = 'n' # qNaN
- else: # sig == 2
- self._exp = 'N' # sNaN
- self._sign = sign
- self._int = tuple(map(int, diag)) # Diagnostic info
- return self
- try:
- self._sign, self._int, self._exp = _string2exact(value)
- except ValueError:
+ self._int = intpart.lstrip('0') or '0'
+ self._exp = exp
+ self._is_special = False
+ else:
+ diag = m.group('diag')
+ if diag is not None:
+ # NaN
+ self._int = diag.lstrip('0')
+ if m.group('signal'):
+ self._exp = 'N'
+ else:
+ self._exp = 'n'
+ else:
+ # infinity
+ self._int = '0'
+ self._exp = 'F'
self._is_special = True
- return context._raise_error(ConversionSyntax,
- "Invalid literal for Decimal: %r" % value)
return self
raise TypeError("Cannot convert %r to Decimal" % value)
@@ -709,7 +725,7 @@ class Decimal(object):
NaNs and infinities are considered nonzero.
"""
- return self._is_special or self._int[0] != 0
+ return self._is_special or self._int != '0'
def __cmp__(self, other):
other = _convert_other(other)
@@ -743,8 +759,8 @@ class Decimal(object):
self_adjusted = self.adjusted()
other_adjusted = other.adjusted()
if self_adjusted == other_adjusted:
- self_padded = self._int + (0,)*(self._exp - other._exp)
- other_padded = other._int + (0,)*(other._exp - self._exp)
+ self_padded = self._int + '0'*(self._exp - other._exp)
+ other_padded = other._int + '0'*(other._exp - self._exp)
return cmp(self_padded, other_padded) * (-1)**self._sign
elif self_adjusted > other_adjusted:
return (-1)**self._sign
@@ -827,7 +843,7 @@ class Decimal(object):
To show the internals exactly as they are.
"""
- return (self._sign, self._int, self._exp)
+ return (self._sign, tuple(map(int, self._int)), self._exp)
def __repr__(self):
"""Represents the number as an instance of Decimal."""
@@ -843,10 +859,10 @@ class Decimal(object):
if self._is_special:
if self._isnan():
minus = '-'*self._sign
- if self._int == (0,):
+ if self._int == '0':
info = ''
else:
- info = ''.join(map(str, self._int))
+ info = self._int
if self._isnan() == 2:
return minus + 'sNaN' + info
return minus + 'NaN' + info
@@ -857,7 +873,7 @@ class Decimal(object):
if context is None:
context = getcontext()
- tmp = list(map(str, self._int))
+ tmp = list(self._int)
numdigits = len(self._int)
leftdigits = self._exp + numdigits
if eng and not self: # self = 0eX wants 0[.0[0]]eY, not [[0]0]0eY
@@ -1030,7 +1046,7 @@ class Decimal(object):
sign = min(self._sign, other._sign)
if negativezero:
sign = 1
- ans = Decimal( (sign, (0,), exp))
+ ans = _dec_from_triple(sign, '0', exp)
if shouldround:
ans = ans._fix(context)
return ans
@@ -1055,7 +1071,7 @@ class Decimal(object):
if op1.sign != op2.sign:
# Equal and opposite
if op1.int == op2.int:
- ans = Decimal((negativezero, (0,), exp))
+ ans = _dec_from_triple(negativezero, '0', exp)
if shouldround:
ans = ans._fix(context)
return ans
@@ -1121,7 +1137,7 @@ class Decimal(object):
For example:
Decimal('5.624e10')._increment() == Decimal('5.625e10')
"""
- L = list(self._int)
+ L = list(map(int, self._int))
L[-1] += 1
spot = len(L)-1
while L[spot] == 10:
@@ -1131,7 +1147,7 @@ class Decimal(object):
break
L[spot-1] += 1
spot -= 1
- return Decimal((self._sign, L, self._exp))
+ return _dec_from_triple(self._sign, "".join(map(str, L)), self._exp)
def __mul__(self, other, context=None):
"""Return self * other.
@@ -1167,20 +1183,20 @@ class Decimal(object):
# Special case for multiplying by zero
if not self or not other:
- ans = Decimal((resultsign, (0,), resultexp))
+ ans = _dec_from_triple(resultsign, '0', resultexp)
if shouldround:
# Fixing in case the exponent is out of bounds
ans = ans._fix(context)
return ans
# Special case for multiplying by power of 10
- if self._int == (1,):
- ans = Decimal((resultsign, other._int, resultexp))
+ if self._int == '1':
+ ans = _dec_from_triple(resultsign, other._int, resultexp)
if shouldround:
ans = ans._fix(context)
return ans
- if other._int == (1,):
- ans = Decimal((resultsign, self._int, resultexp))
+ if other._int == '1':
+ ans = _dec_from_triple(resultsign, self._int, resultexp)
if shouldround:
ans = ans._fix(context)
return ans
@@ -1188,9 +1204,7 @@ class Decimal(object):
op1 = _WorkRep(self)
op2 = _WorkRep(other)
- ans = Decimal((resultsign,
- tuple(map(int, str(op1.int * op2.int))),
- resultexp))
+ ans = _dec_from_triple(resultsign, str(op1.int * op2.int), resultexp)
if shouldround:
ans = ans._fix(context)
@@ -1221,7 +1235,7 @@ class Decimal(object):
if other._isinfinity():
context._raise_error(Clamped, 'Division by infinity')
- return Decimal((sign, (0,), context.Etiny()))
+ return _dec_from_triple(sign, '0', context.Etiny())
# Special cases for zeroes
if not other:
@@ -1253,7 +1267,7 @@ class Decimal(object):
coeff //= 10
exp += 1
- ans = Decimal((sign, list(map(int, str(coeff))), exp))
+ ans = _dec_from_triple(sign, str(coeff), exp)
return ans._fix(context)
def _divide(self, other, context):
@@ -1270,7 +1284,7 @@ class Decimal(object):
expdiff = self.adjusted() - other.adjusted()
if not self or other._isinfinity() or expdiff <= -2:
- return (Decimal((sign, (0,), 0)),
+ return (_dec_from_triple(sign, '0', 0),
self._rescale(ideal_exp, context.rounding))
if expdiff <= context.prec:
op1 = _WorkRep(self)
@@ -1281,9 +1295,8 @@ class Decimal(object):
op2.int *= 10**(op2.exp - op1.exp)
q, r = divmod(op1.int, op2.int)
if q < 10**context.prec:
- return (Decimal((sign, list(map(int, str(q))), 0)),
- Decimal((self._sign, list(map(int, str(r))),
- ideal_exp)))
+ return (_dec_from_triple(sign, str(q), 0),
+ _dec_from_triple(self._sign, str(r), ideal_exp))
# Here the quotient is too large to be representable
ans = context._raise_error(DivisionImpossible,
@@ -1411,7 +1424,7 @@ class Decimal(object):
# self = 0 -> remainder = self, with ideal exponent
ideal_exponent = min(self._exp, other._exp)
if not self:
- ans = Decimal((self._sign, (0,), ideal_exponent))
+ ans = _dec_from_triple(self._sign, '0', ideal_exponent)
return ans._fix(context)
# catch most cases of large or small quotient
@@ -1448,7 +1461,7 @@ class Decimal(object):
sign = 1-sign
r = -r
- ans = Decimal((sign, list(map(int, str(r))), ideal_exponent))
+ ans = _dec_from_triple(sign, str(r), ideal_exponent)
return ans._fix(context)
def __floordiv__(self, other, context=None):
@@ -1500,9 +1513,9 @@ class Decimal(object):
raise OverflowError("Cannot convert infinity to int")
s = (-1)**self._sign
if self._exp >= 0:
- return s*int(''.join(map(str, self._int)))*10**self._exp
+ return s*int(self._int)*10**self._exp
else:
- return s*int(''.join(map(str, self._int))[:self._exp] or '0')
+ return s*int(self._int[:self._exp] or '0')
def _fix_nan(self, context):
"""Decapitate the payload of a NaN to fit the context"""
@@ -1512,11 +1525,8 @@ class Decimal(object):
# precision-1 if _clamp=1.
max_payload_len = context.prec - context._clamp
if len(payload) > max_payload_len:
- pos = len(payload)-max_payload_len
- while pos < len(payload) and payload[pos] == 0:
- pos += 1
- payload = payload[pos:]
- return Decimal((self._sign, payload, self._exp))
+ payload = payload[len(payload)-max_payload_len:].lstrip('0')
+ return _dec_from_triple(self._sign, payload, self._exp, True)
return Decimal(self)
def _fix(self, context):
@@ -1549,7 +1559,7 @@ class Decimal(object):
new_exp = min(max(self._exp, Etiny), exp_max)
if new_exp != self._exp:
context._raise_error(Clamped)
- return Decimal((self._sign, (0,), new_exp))
+ return _dec_from_triple(self._sign, '0', new_exp)
else:
return Decimal(self)
@@ -1581,7 +1591,8 @@ class Decimal(object):
# we get here only if rescaling rounds the
# cofficient up to exactly 10**context.prec
if ans._exp < Etop:
- ans = Decimal((ans._sign, ans._int[:-1], ans._exp+1))
+ ans = _dec_from_triple(ans._sign,
+ ans._int[:-1], ans._exp+1)
else:
# Inexact and Rounded have already been raised
ans = context._raise_error(Overflow, 'above Emax',
@@ -1591,8 +1602,8 @@ class Decimal(object):
# fold down if _clamp == 1 and self has too few digits
if context._clamp == 1 and self._exp > Etop:
context._raise_error(Clamped)
- self_padded = self._int + (0,)*(self._exp - Etop)
- return Decimal((self._sign, self_padded, Etop))
+ self_padded = self._int + '0'*(self._exp - Etop)
+ return _dec_from_triple(self._sign, self_padded, Etop)
# here self was representable to begin with; return unchanged
return Decimal(self)
@@ -1607,29 +1618,29 @@ class Decimal(object):
def _round_down(self, prec):
"""Also known as round-towards-0, truncate."""
newexp = self._exp + len(self._int) - prec
- return Decimal((self._sign, self._int[:prec] or (0,), newexp))
+ return _dec_from_triple(self._sign, self._int[:prec] or '0', newexp)
def _round_up(self, prec):
"""Rounds away from 0."""
newexp = self._exp + len(self._int) - prec
- tmp = Decimal((self._sign, self._int[:prec] or (0,), newexp))
+ tmp = _dec_from_triple(self._sign, self._int[:prec] or '0', newexp)
for digit in self._int[prec:]:
- if digit != 0:
+ if digit != '0':
return tmp._increment()
return tmp
def _round_half_up(self, prec):
"""Rounds 5 up (away from 0)"""
- if self._int[prec] >= 5:
+ if self._int[prec] in '56789':
return self._round_up(prec)
else:
return self._round_down(prec)
def _round_half_down(self, prec):
"""Round 5 down"""
- if self._int[prec] == 5:
+ if self._int[prec] == '5':
for digit in self._int[prec+1:]:
- if digit != 0:
+ if digit != '0':
break
else:
return self._round_down(prec)
@@ -1637,7 +1648,7 @@ class Decimal(object):
def _round_half_even(self, prec):
"""Round 5 to even, rest to nearest."""
- if prec and self._int[prec-1] & 1:
+ if prec and self._int[prec-1] in '13579':
return self._round_half_up(prec)
else:
return self._round_half_down(prec)
@@ -1658,7 +1669,7 @@ class Decimal(object):
def _round_05up(self, prec):
"""Round down unless digit prec-1 is 0 or 5."""
- if prec == 0 or self._int[prec-1] in (0, 5):
+ if prec == 0 or self._int[prec-1] in '05':
return self._round_up(prec)
else:
return self._round_down(prec)
@@ -1776,7 +1787,7 @@ class Decimal(object):
base = pow(base, 10, modulo)
base = pow(base, exponent.int, modulo)
- return Decimal((sign, list(map(int, str(base))), 0))
+ return _dec_from_triple(sign, str(base), 0)
def _power_exact(self, other, p):
"""Attempt to compute self**other exactly.
@@ -1866,7 +1877,7 @@ class Decimal(object):
zeros = min(exponent-ideal_exponent, p-1)
else:
zeros = 0
- return Decimal((0, (1,) + (0,)*zeros, exponent-zeros))
+ return _dec_from_triple(0, '1' + '0'*zeros, exponent-zeros)
# case where y is negative: xc must be either a power
# of 2 or a power of 5.
@@ -1927,7 +1938,7 @@ class Decimal(object):
if xc >= 10**p:
return None
xe = -e-xe
- return Decimal((0, list(map(int, str(xc))), xe))
+ return _dec_from_triple(0, str(xc), xe)
# now y is positive; find m and n such that y = m/n
if ye >= 0:
@@ -1989,7 +2000,7 @@ class Decimal(object):
zeros = min(xe-ideal_exponent, p-len(str_xc))
else:
zeros = 0
- return Decimal((0, list(map(int, str_xc))+[0,]*zeros, xe-zeros))
+ return _dec_from_triple(0, str_xc+'0'*zeros, xe-zeros)
def __pow__(self, other, modulo=None, context=None):
"""Return self ** other [ % modulo].
@@ -2050,12 +2061,12 @@ class Decimal(object):
return context._raise_error(InvalidOperation,
'x ** y with x negative and y not an integer')
# negate self, without doing any unwanted rounding
- self = Decimal((0, self._int, self._exp))
+ self = self.copy_negate()
# 0**(+ve or Inf)= 0; 0**(-ve or -Inf) = Infinity
if not self:
if other._sign == 0:
- return Decimal((result_sign, (0,), 0))
+ return _dec_from_triple(result_sign, '0', 0)
else:
return Infsign[result_sign]
@@ -2064,7 +2075,7 @@ class Decimal(object):
if other._sign == 0:
return Infsign[result_sign]
else:
- return Decimal((result_sign, (0,), 0))
+ return _dec_from_triple(result_sign, '0', 0)
# 1**other = 1, but the choice of exponent and the flags
# depend on the exponent of self, and on whether other is a
@@ -2091,7 +2102,7 @@ class Decimal(object):
context._raise_error(Rounded)
exp = 1-context.prec
- return Decimal((result_sign, (1,)+(0,)*-exp, exp))
+ return _dec_from_triple(result_sign, '1'+'0'*-exp, exp)
# compute adjusted exponent of self
self_adj = self.adjusted()
@@ -2100,7 +2111,7 @@ class Decimal(object):
# self ** -infinity is infinity if self < 1, 0 if self > 1
if other._isinfinity():
if (other._sign == 0) == (self_adj < 0):
- return Decimal((result_sign, (0,), 0))
+ return _dec_from_triple(result_sign, '0', 0)
else:
return Infsign[result_sign]
@@ -2118,19 +2129,19 @@ class Decimal(object):
# self > 1 and other +ve, or self < 1 and other -ve
# possibility of overflow
if bound >= len(str(context.Emax)):
- ans = Decimal((result_sign, (1,), context.Emax+1))
+ ans = _dec_from_triple(result_sign, '1', context.Emax+1)
else:
# self > 1 and other -ve, or self < 1 and other +ve
# possibility of underflow to 0
Etiny = context.Etiny()
if bound >= len(str(-Etiny)):
- ans = Decimal((result_sign, (1,), Etiny-1))
+ ans = _dec_from_triple(result_sign, '1', Etiny-1)
# 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 = Decimal((1, ans._int, ans._exp))
+ ans = _dec_from_triple(1, ans._int, ans._exp)
# usual case: inexact result, x**y computed directly as exp(y*log(x))
if ans is None:
@@ -2151,7 +2162,7 @@ class Decimal(object):
break
extra += 3
- ans = Decimal((result_sign, list(map(int, str(coeff))), exp))
+ ans = _dec_from_triple(result_sign, str(coeff), exp)
# the specification says that for non-integer other we need to
# raise Inexact, even when the result is actually exact. In
@@ -2163,7 +2174,8 @@ class Decimal(object):
# pad with zeros up to length context.prec+1 if necessary
if len(ans._int) <= context.prec:
expdiff = context.prec+1 - len(ans._int)
- ans = Decimal((ans._sign, ans._int+(0,)*expdiff, ans._exp-expdiff))
+ ans = _dec_from_triple(ans._sign, ans._int+'0'*expdiff,
+ ans._exp-expdiff)
if ans.adjusted() < context.Emin:
context._raise_error(Underflow)
@@ -2195,14 +2207,14 @@ class Decimal(object):
return dup
if not dup:
- return Decimal( (dup._sign, (0,), 0) )
+ return _dec_from_triple(dup._sign, '0', 0)
exp_max = [context.Emax, context.Etop()][context._clamp]
end = len(dup._int)
exp = dup._exp
- while dup._int[end-1] == 0 and exp < exp_max:
+ while dup._int[end-1] == '0' and exp < exp_max:
exp += 1
end -= 1
- return Decimal( (dup._sign, dup._int[:end], exp) )
+ return _dec_from_triple(dup._sign, dup._int[:end], exp)
def quantize(self, exp, rounding=None, context=None, watchexp=True):
"""Quantize self so its exponent is the same as that of exp.
@@ -2243,7 +2255,7 @@ class Decimal(object):
'target exponent out of bounds in quantize')
if not self:
- ans = Decimal((self._sign, (0,), exp._exp))
+ ans = _dec_from_triple(self._sign, '0', exp._exp)
return ans._fix(context)
self_adjusted = self.adjusted()
@@ -2303,17 +2315,18 @@ class Decimal(object):
if self._is_special:
return Decimal(self)
if not self:
- return Decimal((self._sign, (0,), exp))
+ return _dec_from_triple(self._sign, '0', exp)
if self._exp >= exp:
# pad answer with zeros if necessary
- return Decimal((self._sign, self._int + (0,)*(self._exp - exp), exp))
+ return _dec_from_triple(self._sign,
+ self._int + '0'*(self._exp - exp), exp)
# too many digits; round and lose data. If self.adjusted() <
# exp-1, replace self by 10**(exp-1) before rounding
digits = len(self._int) + self._exp - exp
if digits < 0:
- self = Decimal((self._sign, (1,), exp-1))
+ self = _dec_from_triple(self._sign, '1', exp-1)
digits = 0
this_function = getattr(self, self._pick_rounding_function[rounding])
return this_function(digits)
@@ -2336,7 +2349,7 @@ class Decimal(object):
if self._exp >= 0:
return Decimal(self)
if not self:
- return Decimal((self._sign, (0,), 0))
+ return _dec_from_triple(self._sign, '0', 0)
if context is None:
context = getcontext()
if rounding is None:
@@ -2378,7 +2391,7 @@ class Decimal(object):
if not self:
# exponent = self._exp // 2. sqrt(-0) = -0
- ans = Decimal((self._sign, (0,), self._exp // 2))
+ ans = _dec_from_triple(self._sign, '0', self._exp // 2)
return ans._fix(context)
if context is None:
@@ -2455,7 +2468,7 @@ class Decimal(object):
if n % 5 == 0:
n += 1
- ans = Decimal((0, list(map(int, str(n))), e))
+ ans = _dec_from_triple(0, str(n), e)
# round, and fit to current context
context = context._shallow_copy()
@@ -2552,13 +2565,13 @@ class Decimal(object):
if self._exp >= 0:
return True
rest = self._int[self._exp:]
- return rest == (0,)*len(rest)
+ return rest == '0'*len(rest)
def _iseven(self):
"""Returns True if self is even. Assumes self is an integer."""
if not self or self._exp > 0:
return True
- return self._int[-1+self._exp] & 1 == 0
+ return self._int[-1+self._exp] in '02468'
def adjusted(self):
"""Return the adjusted exponent of self"""
@@ -2680,18 +2693,19 @@ class Decimal(object):
def copy_abs(self):
"""Returns a copy with the sign set to 0. """
- return Decimal((0, self._int, self._exp))
+ return _dec_from_triple(0, self._int, self._exp, self._is_special)
def copy_negate(self):
"""Returns a copy with the sign inverted."""
if self._sign:
- return Decimal((0, self._int, self._exp))
+ return _dec_from_triple(0, self._int, self._exp, self._is_special)
else:
- return Decimal((1, self._int, self._exp))
+ return _dec_from_triple(1, self._int, self._exp, self._is_special)
def copy_sign(self, other):
"""Returns self with the sign of other."""
- return Decimal((other._sign, self._int, self._exp))
+ return _dec_from_triple(other._sign, self._int,
+ self._exp, self._is_special)
def exp(self, context=None):
"""Returns e ** self."""
@@ -2730,16 +2744,16 @@ class Decimal(object):
# larger exponent the result either overflows or underflows.
if self._sign == 0 and adj > len(str((context.Emax+1)*3)):
# overflow
- ans = Decimal((0, (1,), context.Emax+1))
+ ans = _dec_from_triple(0, '1', context.Emax+1)
elif self._sign == 1 and adj > len(str((-context.Etiny()+1)*3)):
# underflow to 0
- ans = Decimal((0, (1,), context.Etiny()-1))
+ ans = _dec_from_triple(0, '1', context.Etiny()-1)
elif self._sign == 0 and adj < -p:
# p+1 digits; final round will raise correct flags
- ans = Decimal((0, (1,) + (0,)*(p-1) + (1,), -p))
+ ans = _dec_from_triple(0, '1' + '0'*(p-1) + '1', -p)
elif self._sign == 1 and adj < -p-1:
# p+1 digits; final round will raise correct flags
- ans = Decimal((0, (9,)*(p+1), -p-1))
+ ans = _dec_from_triple(0, '9'*(p+1), -p-1)
# general case
else:
op = _WorkRep(self)
@@ -2757,7 +2771,7 @@ class Decimal(object):
break
extra += 3
- ans = Decimal((0, list(map(int, str(coeff))), exp))
+ ans = _dec_from_triple(0, str(coeff), exp)
# at this stage, ans should round correctly with *any*
# rounding mode, not just with ROUND_HALF_EVEN
@@ -2822,7 +2836,7 @@ class Decimal(object):
def is_zero(self):
"""Return True if self is a zero; otherwise return False."""
- return not self._is_special and self._int[0] == 0
+ return not self._is_special and self._int == '0'
def _ln_exp_bound(self):
"""Compute a lower bound for the adjusted exponent of self.ln().
@@ -2891,7 +2905,7 @@ class Decimal(object):
if coeff % (5*10**(len(str(abs(coeff)))-p-1)):
break
places += 3
- ans = Decimal((int(coeff<0), list(map(int, str(abs(coeff)))), -places))
+ ans = _dec_from_triple(int(coeff<0), str(abs(coeff)), -places)
context = context._shallow_copy()
rounding = context._set_rounding(ROUND_HALF_EVEN)
@@ -2954,7 +2968,7 @@ class Decimal(object):
'log10 of a negative value')
# log10(10**n) = n
- if self._int[0] == 1 and self._int[1:] == (0,)*(len(self._int) - 1):
+ if self._int[0] == '1' and self._int[1:] == '0'*(len(self._int) - 1):
# answer may need rounding
ans = Decimal(self._exp + len(self._int) - 1)
else:
@@ -2972,8 +2986,7 @@ class Decimal(object):
if coeff % (5*10**(len(str(abs(coeff)))-p-1)):
break
places += 3
- ans = Decimal((int(coeff<0), list(map(int, str(abs(coeff)))),
- -places))
+ ans = _dec_from_triple(int(coeff<0), str(abs(coeff)), -places)
context = context._shallow_copy()
rounding = context._set_rounding(ROUND_HALF_EVEN)
@@ -3020,19 +3033,19 @@ class Decimal(object):
if self._sign != 0 or self._exp != 0:
return False
for dig in self._int:
- if dig not in (0, 1):
+ if dig not in '01':
return False
return True
def _fill_logical(self, context, opa, opb):
dif = context.prec - len(opa)
if dif > 0:
- opa = (0,)*dif + opa
+ opa = '0'*dif + opa
elif dif < 0:
opa = opa[-context.prec:]
dif = context.prec - len(opb)
if dif > 0:
- opb = (0,)*dif + opb
+ opb = '0'*dif + opb
elif dif < 0:
opb = opb[-context.prec:]
return opa, opb
@@ -3048,22 +3061,15 @@ class Decimal(object):
(opa, opb) = self._fill_logical(context, self._int, other._int)
# make the operation, and clean starting zeroes
- result = [a&b for a,b in zip(opa,opb)]
- for i,d in enumerate(result):
- if d == 1:
- break
- result = tuple(result[i:])
-
- # if empty, we must have at least a zero
- if not result:
- result = (0,)
- return Decimal((0, result, 0))
+ result = "".join([str(int(a)&int(b)) for a,b in zip(opa,opb)])
+ return _dec_from_triple(0, result.lstrip('0') or '0', 0)
def logical_invert(self, context=None):
"""Invert all its digits."""
if context is None:
context = getcontext()
- return self.logical_xor(Decimal((0,(1,)*context.prec,0)), context)
+ return self.logical_xor(_dec_from_triple(0,'1'*context.prec,0),
+ context)
def logical_or(self, other, context=None):
"""Applies an 'or' operation between self and other's digits."""
@@ -3076,16 +3082,8 @@ class Decimal(object):
(opa, opb) = self._fill_logical(context, self._int, other._int)
# make the operation, and clean starting zeroes
- result = [a|b for a,b in zip(opa,opb)]
- for i,d in enumerate(result):
- if d == 1:
- break
- result = tuple(result[i:])
-
- # if empty, we must have at least a zero
- if not result:
- result = (0,)
- return Decimal((0, result, 0))
+ result = "".join(str(int(a)|int(b)) for a,b in zip(opa,opb))
+ return _dec_from_triple(0, result.lstrip('0') or '0', 0)
def logical_xor(self, other, context=None):
"""Applies an 'xor' operation between self and other's digits."""
@@ -3098,16 +3096,8 @@ class Decimal(object):
(opa, opb) = self._fill_logical(context, self._int, other._int)
# make the operation, and clean starting zeroes
- result = [a^b for a,b in zip(opa,opb)]
- for i,d in enumerate(result):
- if d == 1:
- break
- result = tuple(result[i:])
-
- # if empty, we must have at least a zero
- if not result:
- result = (0,)
- return Decimal((0, result, 0))
+ result = "".join(str(int(a)^int(b)) for a,b in zip(opa,opb))
+ return _dec_from_triple(0, result.lstrip('0') or '0', 0)
def max_mag(self, other, context=None):
"""Compares the values numerically with their sign ignored."""
@@ -3185,7 +3175,7 @@ class Decimal(object):
if self._isinfinity() == -1:
return negInf
if self._isinfinity() == 1:
- return Decimal((0, (9,)*context.prec, context.Etop()))
+ return _dec_from_triple(0, '9'*context.prec, context.Etop())
context = context.copy()
context._set_rounding(ROUND_FLOOR)
@@ -3193,7 +3183,8 @@ class Decimal(object):
new_self = self._fix(context)
if new_self != self:
return new_self
- return self.__sub__(Decimal((0, (1,), context.Etiny()-1)), context)
+ return self.__sub__(_dec_from_triple(0, '1', context.Etiny()-1),
+ context)
def next_plus(self, context=None):
"""Returns the smallest representable number larger than itself."""
@@ -3207,7 +3198,7 @@ class Decimal(object):
if self._isinfinity() == 1:
return Inf
if self._isinfinity() == -1:
- return Decimal((1, (9,)*context.prec, context.Etop()))
+ return _dec_from_triple(1, '9'*context.prec, context.Etop())
context = context.copy()
context._set_rounding(ROUND_CEILING)
@@ -3215,7 +3206,8 @@ class Decimal(object):
new_self = self._fix(context)
if new_self != self:
return new_self
- return self.__add__(Decimal((0, (1,), context.Etiny()-1)), context)
+ return self.__add__(_dec_from_triple(0, '1', context.Etiny()-1),
+ context)
def next_toward(self, other, context=None):
"""Returns the number closest to self, in the direction towards other.
@@ -3237,7 +3229,7 @@ class Decimal(object):
comparison = self.__cmp__(other)
if comparison == 0:
- return Decimal((other._sign, self._int, self._exp))
+ return self.copy_sign(other)
if comparison == -1:
ans = self.next_plus(context)
@@ -3331,19 +3323,12 @@ class Decimal(object):
rotdig = self._int
topad = context.prec - len(rotdig)
if topad:
- rotdig = ((0,)*topad) + rotdig
+ rotdig = '0'*topad + rotdig
# let's rotate!
rotated = rotdig[torot:] + rotdig[:torot]
-
- # clean starting zeroes
- for i,d in enumerate(rotated):
- if d != 0:
- break
- rotated = rotated[i:]
-
- return Decimal((self._sign, rotated, self._exp))
-
+ return _dec_from_triple(self._sign,
+ rotated.lstrip('0') or '0', self._exp)
def scaleb (self, other, context=None):
"""Returns self operand after adding the second value to its exp."""
@@ -3364,7 +3349,7 @@ class Decimal(object):
if self._isinfinity():
return Decimal(self)
- d = Decimal((self._sign, self._int, self._exp + int(other)))
+ d = _dec_from_triple(self._sign, self._int, self._exp + int(other))
d = d._fix(context)
return d
@@ -3392,26 +3377,17 @@ class Decimal(object):
rotdig = self._int
topad = context.prec - len(rotdig)
if topad:
- rotdig = ((0,)*topad) + rotdig
+ rotdig = '0'*topad + rotdig
# let's shift!
if torot < 0:
rotated = rotdig[:torot]
else:
- rotated = (rotdig + ((0,) * torot))
+ rotated = rotdig + '0'*torot
rotated = rotated[-context.prec:]
- # clean starting zeroes
- if rotated:
- for i,d in enumerate(rotated):
- if d != 0:
- break
- rotated = rotated[i:]
- else:
- rotated = (0,)
-
- return Decimal((self._sign, rotated, self._exp))
-
+ return _dec_from_triple(self._sign,
+ rotated.lstrip('0') or '0', self._exp)
# Support for pickling, copy, and deepcopy
def __reduce__(self):
@@ -3427,6 +3403,22 @@ class Decimal(object):
return self # My components are also immutable
return self.__class__(str(self))
+def _dec_from_triple(sign, coefficient, exponent, special=False):
+ """Create a decimal instance directly, without any validation,
+ normalization (e.g. removal of leading zeros) or argument
+ conversion.
+
+ This function is for *internal use only*.
+ """
+
+ self = object.__new__(Decimal)
+ self._sign = sign
+ self._int = coefficient
+ self._exp = exponent
+ self._is_special = special
+
+ return self
+
##### Context class #######################################################
@@ -4775,10 +4767,7 @@ class _WorkRep(object):
self.exp = None
elif isinstance(value, Decimal):
self.sign = value._sign
- cum = 0
- for digit in value._int:
- cum = cum * 10 + digit
- self.int = cum
+ self.int = int(value._int)
self.exp = value._exp
else:
# assert isinstance(value, tuple)
@@ -5175,53 +5164,6 @@ def _convert_other(other, raiseit=False):
raise TypeError("Unable to convert %s to Decimal" % other)
return NotImplemented
-_infinity_map = {
- 'inf' : 1,
- 'infinity' : 1,
- '+inf' : 1,
- '+infinity' : 1,
- '-inf' : -1,
- '-infinity' : -1
-}
-
-def _isinfinity(num):
- """Determines whether a string or float is infinity.
-
- +1 for negative infinity; 0 for finite ; +1 for positive infinity
- """
- num = str(num).lower()
- return _infinity_map.get(num, 0)
-
-def _isnan(num):
- """Determines whether a string or float is NaN
-
- (1, sign, diagnostic info as string) => NaN
- (2, sign, diagnostic info as string) => sNaN
- 0 => not a NaN
- """
- num = str(num).lower()
- if not num:
- return 0
-
- # Get the sign, get rid of trailing [+-]
- sign = 0
- if num[0] == '+':
- num = num[1:]
- elif num[0] == '-': # elif avoids '+-nan'
- num = num[1:]
- sign = 1
-
- if num.startswith('nan'):
- if len(num) > 3 and not num[3:].isdigit(): # diagnostic info
- return 0
- return (1, sign, num[3:].lstrip('0'))
- if num.startswith('snan'):
- if len(num) > 4 and not num[4:].isdigit():
- return 0
- return (2, sign, num[4:].lstrip('0'))
- return 0
-
-
##### Setup Specific Contexts ############################################
# The default context prototype used by Context()
@@ -5255,91 +5197,62 @@ ExtendedContext = Context(
)
-##### Useful Constants (internal use only) ################################
-
-# Reusable defaults
-Inf = Decimal('Inf')
-negInf = Decimal('-Inf')
-NaN = Decimal('NaN')
-Dec_0 = Decimal(0)
-Dec_p1 = Decimal(1)
-Dec_n1 = Decimal(-1)
-Dec_p2 = Decimal(2)
-Dec_n2 = Decimal(-2)
-
-# Infsign[sign] is infinity w/ that sign
-Infsign = (Inf, negInf)
-
-
##### crud for parsing strings #############################################
import re
-# There's an optional sign at the start, and an optional exponent
-# at the end. The exponent has an optional sign and at least one
-# digit. In between, must have either at least one digit followed
-# by an optional fraction, or a decimal point followed by at least
-# one digit. Yuck.
+# Regular expression used for parsing numeric strings. Additional
+# comments:
+#
+# 1. Uncomment the two '\s*' lines to allow leading and/or trailing
+# whitespace. But note that the specification disallows whitespace in
+# a numeric string.
+#
+# 2. For finite numbers (not infinities and NaNs) the body of the
+# number between the optional sign and the optional exponent must have
+# at least one decimal digit, possibly after the decimal point. The
+# lookahead expression '(?=\d|\.\d)' checks this.
+#
+# As the flag UNICODE is not enabled here, we're explicitly avoiding any
+# other meaning for \d than the numbers [0-9].
-_parser = re.compile(r"""
+import re
+_parser = re.compile(r""" # A numeric string consists of:
# \s*
- (?P<sign>[-+])?
+ (?P<sign>[-+])? # an optional sign, followed by either...
(
- (?P<int>\d+) (\. (?P<frac>\d*))?
+ (?=\d|\.\d) # ...a number (with at least one digit)
+ (?P<int>\d*) # consisting of a (possibly empty) integer part
+ (\.(?P<frac>\d*))? # followed by an optional fractional part
+ (E(?P<exp>[-+]?\d+))? # followed by an optional exponent, or...
|
- \. (?P<onlyfrac>\d+)
+ Inf(inity)? # ...an infinity, or...
+ |
+ (?P<signal>s)? # ...an (optionally signaling)
+ NaN # NaN
+ (?P<diag>\d*) # with (possibly empty) diagnostic information.
)
- ([eE](?P<exp>[-+]? \d+))?
# \s*
$
-""", re.VERBOSE).match # Uncomment the \s* to allow leading or trailing spaces.
+""", re.VERBOSE | re.IGNORECASE).match
del re
-def _string2exact(s):
- """Return sign, n, p s.t.
- Float string value == -1**sign * n * 10**p exactly
- """
- m = _parser(s)
- if m is None:
- raise ValueError("invalid literal for Decimal: %r" % s)
+##### Useful Constants (internal use only) ################################
- if m.group('sign') == "-":
- sign = 1
- else:
- sign = 0
+# Reusable defaults
+Inf = Decimal('Inf')
+negInf = Decimal('-Inf')
+NaN = Decimal('NaN')
+Dec_0 = Decimal(0)
+Dec_p1 = Decimal(1)
+Dec_n1 = Decimal(-1)
+Dec_p2 = Decimal(2)
+Dec_n2 = Decimal(-2)
- exp = m.group('exp')
- if exp is None:
- exp = 0
- else:
- exp = int(exp)
+# Infsign[sign] is infinity w/ that sign
+Infsign = (Inf, negInf)
- intpart = m.group('int')
- if intpart is None:
- intpart = ""
- fracpart = m.group('onlyfrac')
- else:
- fracpart = m.group('frac')
- if fracpart is None:
- fracpart = ""
-
- exp -= len(fracpart)
-
- mantissa = intpart + fracpart
- tmp = list(map(int, mantissa))
- backup = tmp
- while tmp and tmp[0] == 0:
- del tmp[0]
-
- # It's a zero
- if not tmp:
- if backup:
- return (sign, tuple(backup), exp)
- return (sign, (0,), exp)
- mantissa = tuple(tmp)
-
- return (sign, mantissa, exp)
if __name__ == '__main__':