summaryrefslogtreecommitdiff
path: root/Lib/decimal.py
diff options
context:
space:
mode:
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 faf9bf71d9..7842cb2507 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__':