Kaydet (Commit) ac4ae4ba authored tarafından Facundo Batista's avatar Facundo Batista

Comments and docs cleanups, and some little fixes, provided by Santiágo Peresón

üst caebe220
...@@ -29,8 +29,8 @@ and IEEE standard 854-1987: ...@@ -29,8 +29,8 @@ and IEEE standard 854-1987:
Decimal floating point has finite precision with arbitrarily large bounds. Decimal floating point has finite precision with arbitrarily large bounds.
The purpose of the module is to support arithmetic using familiar The purpose of this module is to support arithmetic using familiar
"schoolhouse" rules and to avoid the some of tricky representation "schoolhouse" rules and to avoid some of the tricky representation
issues associated with binary floating point. The package is especially issues associated with binary floating point. The package is especially
useful for financial applications or for contexts where users have useful for financial applications or for contexts where users have
expectations that are at odds with binary floating point (for instance, expectations that are at odds with binary floating point (for instance,
...@@ -136,7 +136,7 @@ __all__ = [ ...@@ -136,7 +136,7 @@ __all__ = [
import copy as _copy import copy as _copy
#Rounding # Rounding
ROUND_DOWN = 'ROUND_DOWN' ROUND_DOWN = 'ROUND_DOWN'
ROUND_HALF_UP = 'ROUND_HALF_UP' ROUND_HALF_UP = 'ROUND_HALF_UP'
ROUND_HALF_EVEN = 'ROUND_HALF_EVEN' ROUND_HALF_EVEN = 'ROUND_HALF_EVEN'
...@@ -145,11 +145,11 @@ ROUND_FLOOR = 'ROUND_FLOOR' ...@@ -145,11 +145,11 @@ ROUND_FLOOR = 'ROUND_FLOOR'
ROUND_UP = 'ROUND_UP' ROUND_UP = 'ROUND_UP'
ROUND_HALF_DOWN = 'ROUND_HALF_DOWN' ROUND_HALF_DOWN = 'ROUND_HALF_DOWN'
#Rounding decision (not part of the public API) # Rounding decision (not part of the public API)
NEVER_ROUND = 'NEVER_ROUND' # Round in division (non-divmod), sqrt ONLY NEVER_ROUND = 'NEVER_ROUND' # Round in division (non-divmod), sqrt ONLY
ALWAYS_ROUND = 'ALWAYS_ROUND' # Every operation rounds at end. ALWAYS_ROUND = 'ALWAYS_ROUND' # Every operation rounds at end.
#Errors # Errors
class DecimalException(ArithmeticError): class DecimalException(ArithmeticError):
"""Base exception class. """Base exception class.
...@@ -160,17 +160,17 @@ class DecimalException(ArithmeticError): ...@@ -160,17 +160,17 @@ class DecimalException(ArithmeticError):
called if the others are present. This isn't actually used for called if the others are present. This isn't actually used for
anything, though. anything, though.
handle -- Called when context._raise_error is called and the
trap_enabler is set. First argument is self, second is the
context. More arguments can be given, those being after
the explanation in _raise_error (For example,
context._raise_error(NewError, '(-x)!', self._sign) would
call NewError().handle(context, self._sign).)
To define a new exception, it should be sufficient to have it derive To define a new exception, it should be sufficient to have it derive
from DecimalException. from DecimalException.
""" """
def handle(self, context, *args): def handle(self, context, *args):
"""Called when context._raise_error is called and trap_enabler is set.
First argument is self, second is the context. More arguments can
be given, those being after the explanation in _raise_error (For
example, context._raise_error(NewError, '(-x)!', self._sign) would
call NewError().handle(context, self._sign).)
"""
pass pass
...@@ -185,6 +185,7 @@ class Clamped(DecimalException): ...@@ -185,6 +185,7 @@ class Clamped(DecimalException):
this latter case, the exponent is reduced to fit and the corresponding this latter case, the exponent is reduced to fit and the corresponding
number of zero digits are appended to the coefficient ("fold-down"). number of zero digits are appended to the coefficient ("fold-down").
""" """
pass
class InvalidOperation(DecimalException): class InvalidOperation(DecimalException):
...@@ -207,10 +208,11 @@ class InvalidOperation(DecimalException): ...@@ -207,10 +208,11 @@ class InvalidOperation(DecimalException):
""" """
def handle(self, context, *args): def handle(self, context, *args):
if args: if args:
if args[0] == 1: #sNaN, must drop 's' but keep diagnostics if args[0] == 1: # sNaN, must drop 's' but keep diagnostics
return Decimal( (args[1]._sign, args[1]._int, 'n') ) return Decimal( (args[1]._sign, args[1]._int, 'n') )
return NaN return NaN
class ConversionSyntax(InvalidOperation): class ConversionSyntax(InvalidOperation):
"""Trying to convert badly formed string. """Trying to convert badly formed string.
...@@ -218,9 +220,9 @@ class ConversionSyntax(InvalidOperation): ...@@ -218,9 +220,9 @@ class ConversionSyntax(InvalidOperation):
converted to a number and it does not conform to the numeric string converted to a number and it does not conform to the numeric string
syntax. The result is [0,qNaN]. syntax. The result is [0,qNaN].
""" """
def handle(self, context, *args): def handle(self, context, *args):
return (0, (0,), 'n') #Passed to something which uses a tuple. return (0, (0,), 'n') # Passed to something which uses a tuple.
class DivisionByZero(DecimalException, ZeroDivisionError): class DivisionByZero(DecimalException, ZeroDivisionError):
"""Division by 0. """Division by 0.
...@@ -234,12 +236,12 @@ class DivisionByZero(DecimalException, ZeroDivisionError): ...@@ -234,12 +236,12 @@ class DivisionByZero(DecimalException, ZeroDivisionError):
or of the signs of the operands for divide, or is 1 for an odd power of or of the signs of the operands for divide, or is 1 for an odd power of
-0, for power. -0, for power.
""" """
def handle(self, context, sign, double = None, *args): def handle(self, context, sign, double = None, *args):
if double is not None: if double is not None:
return (Infsign[sign],)*2 return (Infsign[sign],)*2
return Infsign[sign] return Infsign[sign]
class DivisionImpossible(InvalidOperation): class DivisionImpossible(InvalidOperation):
"""Cannot perform the division adequately. """Cannot perform the division adequately.
...@@ -247,10 +249,10 @@ class DivisionImpossible(InvalidOperation): ...@@ -247,10 +249,10 @@ class DivisionImpossible(InvalidOperation):
divide-integer or remainder operation had too many digits (would be divide-integer or remainder operation had too many digits (would be
longer than precision). The result is [0,qNaN]. longer than precision). The result is [0,qNaN].
""" """
def handle(self, context, *args): def handle(self, context, *args):
return (NaN, NaN) return (NaN, NaN)
class DivisionUndefined(InvalidOperation, ZeroDivisionError): class DivisionUndefined(InvalidOperation, ZeroDivisionError):
"""Undefined result of division. """Undefined result of division.
...@@ -258,12 +260,12 @@ class DivisionUndefined(InvalidOperation, ZeroDivisionError): ...@@ -258,12 +260,12 @@ class DivisionUndefined(InvalidOperation, ZeroDivisionError):
attempted (during a divide-integer, divide, or remainder operation), and attempted (during a divide-integer, divide, or remainder operation), and
the dividend is also zero. The result is [0,qNaN]. the dividend is also zero. The result is [0,qNaN].
""" """
def handle(self, context, tup=None, *args): def handle(self, context, tup=None, *args):
if tup is not None: if tup is not None:
return (NaN, NaN) #for 0 %0, 0 // 0 return (NaN, NaN) # For 0 %0, 0 // 0
return NaN return NaN
class Inexact(DecimalException): class Inexact(DecimalException):
"""Had to round, losing information. """Had to round, losing information.
...@@ -277,6 +279,7 @@ class Inexact(DecimalException): ...@@ -277,6 +279,7 @@ class Inexact(DecimalException):
""" """
pass pass
class InvalidContext(InvalidOperation): class InvalidContext(InvalidOperation):
"""Invalid context. Unknown rounding, for example. """Invalid context. Unknown rounding, for example.
...@@ -287,10 +290,10 @@ class InvalidContext(InvalidOperation): ...@@ -287,10 +290,10 @@ class InvalidContext(InvalidOperation):
was specified. These aspects of the context need only be checked when was specified. These aspects of the context need only be checked when
the values are required to be used. The result is [0,qNaN]. the values are required to be used. The result is [0,qNaN].
""" """
def handle(self, context, *args): def handle(self, context, *args):
return NaN return NaN
class Rounded(DecimalException): class Rounded(DecimalException):
"""Number got rounded (not necessarily changed during rounding). """Number got rounded (not necessarily changed during rounding).
...@@ -304,6 +307,7 @@ class Rounded(DecimalException): ...@@ -304,6 +307,7 @@ class Rounded(DecimalException):
""" """
pass pass
class Subnormal(DecimalException): class Subnormal(DecimalException):
"""Exponent < Emin before rounding. """Exponent < Emin before rounding.
...@@ -316,6 +320,7 @@ class Subnormal(DecimalException): ...@@ -316,6 +320,7 @@ class Subnormal(DecimalException):
""" """
pass pass
class Overflow(Inexact, Rounded): class Overflow(Inexact, Rounded):
"""Numerical overflow. """Numerical overflow.
...@@ -337,7 +342,6 @@ class Overflow(Inexact, Rounded): ...@@ -337,7 +342,6 @@ class Overflow(Inexact, Rounded):
result is 0, or is [1,inf] otherwise. In all cases, Inexact and Rounded result is 0, or is [1,inf] otherwise. In all cases, Inexact and Rounded
will also be raised. will also be raised.
""" """
def handle(self, context, sign, *args): def handle(self, context, sign, *args):
if context.rounding in (ROUND_HALF_UP, ROUND_HALF_EVEN, if context.rounding in (ROUND_HALF_UP, ROUND_HALF_EVEN,
ROUND_HALF_DOWN, ROUND_UP): ROUND_HALF_DOWN, ROUND_UP):
...@@ -368,6 +372,8 @@ class Underflow(Inexact, Rounded, Subnormal): ...@@ -368,6 +372,8 @@ class Underflow(Inexact, Rounded, Subnormal):
In all cases, Inexact, Rounded, and Subnormal will also be raised. In all cases, Inexact, Rounded, and Subnormal will also be raised.
""" """
pass
# List of public traps and flags # List of public traps and flags
_signals = [Clamped, DivisionByZero, Inexact, Overflow, Rounded, _signals = [Clamped, DivisionByZero, Inexact, Overflow, Rounded,
...@@ -379,32 +385,34 @@ _condition_map = {ConversionSyntax:InvalidOperation, ...@@ -379,32 +385,34 @@ _condition_map = {ConversionSyntax:InvalidOperation,
DivisionUndefined:InvalidOperation, DivisionUndefined:InvalidOperation,
InvalidContext:InvalidOperation} InvalidContext:InvalidOperation}
##### Context Functions ####################################### ##### Context Functions #####################################################
# The getcontext() and setcontext() function manage access to a thread-local # The getcontext() and setcontext() function manage access to a thread-local
# current context. Py2.4 offers direct support for thread locals. If that # current context. Py2.4 offers direct support for thread locals. If that
# is not available, use threading.currentThread() which is slower but will # is not available, use threading.currentThread() which is slower but will
# work for older Pythons. If threads are not part of the build, create a # work for older Pythons. If threads are not part of the build, create a
# mock threading object with threading.local() returning the module namespace. # mock threading object with threading.local() returning the module
# namespace.
try: try:
import threading import threading
except ImportError: except ImportError:
# Python was compiled without threads; create a mock object instead # Python was compiled without threads; create a mock object instead
import sys import sys
class MockThreading: class MockThreading(object):
def local(self, sys=sys): def local(self, sys=sys):
return sys.modules[__name__] return sys.modules[__name__]
threading = MockThreading() threading = MockThreading()
del sys, MockThreading del sys, MockThreading
try: try:
threading.local threading.local
except AttributeError: except AttributeError:
#To fix reloading, force it to create a new context # To fix reloading, force it to create a new context
#Old contexts have different exceptions in their dicts, making problems. # Old contexts have different exceptions in their dicts, making problems.
if hasattr(threading.currentThread(), '__decimal_context__'): if hasattr(threading.currentThread(), '__decimal_context__'):
del threading.currentThread().__decimal_context__ del threading.currentThread().__decimal_context__
...@@ -459,7 +467,7 @@ else: ...@@ -459,7 +467,7 @@ else:
del threading, local # Don't contaminate the namespace del threading, local # Don't contaminate the namespace
##### Decimal class ########################################### ##### Decimal class ##########################################################
class Decimal(object): class Decimal(object):
"""Floating point class for decimal arithmetic.""" """Floating point class for decimal arithmetic."""
...@@ -475,7 +483,7 @@ class Decimal(object): ...@@ -475,7 +483,7 @@ class Decimal(object):
>>> Decimal('3.14') # string input >>> Decimal('3.14') # string input
Decimal("3.14") Decimal("3.14")
>>> Decimal((0, (3, 1, 4), -2)) # tuple input (sign, digit_tuple, exponent) >>> Decimal((0, (3, 1, 4), -2)) # tuple (sign, digit_tuple, exponent)
Decimal("3.14") Decimal("3.14")
>>> Decimal(314) # int or long >>> Decimal(314) # int or long
Decimal("314") Decimal("314")
...@@ -514,12 +522,13 @@ class Decimal(object): ...@@ -514,12 +522,13 @@ class Decimal(object):
# tuple/list conversion (possibly from as_tuple()) # tuple/list conversion (possibly from as_tuple())
if isinstance(value, (list,tuple)): if isinstance(value, (list,tuple)):
if len(value) != 3: if len(value) != 3:
raise ValueError, 'Invalid arguments' raise ValueError('Invalid arguments')
if value[0] not in (0,1): if value[0] not in (0,1):
raise ValueError, 'Invalid sign' raise ValueError('Invalid sign')
for digit in value[1]: for digit in value[1]:
if not isinstance(digit, (int,long)) or digit < 0: if not isinstance(digit, (int,long)) or digit < 0:
raise ValueError, "The second value in the tuple must be composed of non negative integer elements." raise ValueError("The second value in the tuple must be "+
"composed of non negative integer elements.")
self._sign = value[0] self._sign = value[0]
self._int = tuple(value[1]) self._int = tuple(value[1])
...@@ -553,22 +562,23 @@ class Decimal(object): ...@@ -553,22 +562,23 @@ class Decimal(object):
if _isnan(value): if _isnan(value):
sig, sign, diag = _isnan(value) sig, sign, diag = _isnan(value)
self._is_special = True self._is_special = True
if len(diag) > context.prec: #Diagnostic info too long if len(diag) > context.prec: # Diagnostic info too long
self._sign, self._int, self._exp = \ self._sign, self._int, self._exp = \
context._raise_error(ConversionSyntax) context._raise_error(ConversionSyntax)
return self return self
if sig == 1: if sig == 1:
self._exp = 'n' #qNaN self._exp = 'n' #qNaN
else: #sig == 2 else: # sig == 2
self._exp = 'N' #sNaN self._exp = 'N' #sNaN
self._sign = sign self._sign = sign
self._int = tuple(map(int, diag)) #Diagnostic info self._int = tuple(map(int, diag)) # Diagnostic info
return self return self
try: try:
self._sign, self._int, self._exp = _string2exact(value) self._sign, self._int, self._exp = _string2exact(value)
except ValueError: except ValueError:
self._is_special = True self._is_special = True
self._sign, self._int, self._exp = context._raise_error(ConversionSyntax) self._sign, self._int, self._exp = \
context._raise_error(ConversionSyntax)
return self return self
raise TypeError("Cannot convert %r to Decimal" % value) raise TypeError("Cannot convert %r to Decimal" % value)
...@@ -657,9 +667,9 @@ class Decimal(object): ...@@ -657,9 +667,9 @@ class Decimal(object):
return cmp(self._isinfinity(), other._isinfinity()) return cmp(self._isinfinity(), other._isinfinity())
if not self and not other: if not self and not other:
return 0 #If both 0, sign comparison isn't certain. return 0 # If both 0, sign comparison isn't certain.
#If different signs, neg one is less # If different signs, neg one is less
if other._sign < self._sign: if other._sign < self._sign:
return -1 return -1
if self._sign < other._sign: if self._sign < other._sign:
...@@ -670,7 +680,7 @@ class Decimal(object): ...@@ -670,7 +680,7 @@ class Decimal(object):
if self_adjusted == other_adjusted and \ if self_adjusted == other_adjusted and \
self._int + (0,)*(self._exp - other._exp) == \ self._int + (0,)*(self._exp - other._exp) == \
other._int + (0,)*(other._exp - self._exp): other._int + (0,)*(other._exp - self._exp):
return 0 #equal, except in precision. ([0]*(-x) = []) return 0 # Equal, except in precision. ([0]*(-x) = [])
elif self_adjusted > other_adjusted and self._int[0] != 0: elif self_adjusted > other_adjusted and self._int[0] != 0:
return (-1)**self._sign return (-1)**self._sign
elif self_adjusted < other_adjusted and other._int[0] != 0: elif self_adjusted < other_adjusted and other._int[0] != 0:
...@@ -681,7 +691,7 @@ class Decimal(object): ...@@ -681,7 +691,7 @@ class Decimal(object):
context = getcontext() context = getcontext()
context = context._shallow_copy() context = context._shallow_copy()
rounding = context._set_rounding(ROUND_UP) #round away from 0 rounding = context._set_rounding(ROUND_UP) # Round away from 0
flags = context._ignore_all_flags() flags = context._ignore_all_flags()
res = self.__sub__(other, context=context) res = self.__sub__(other, context=context)
...@@ -719,7 +729,7 @@ class Decimal(object): ...@@ -719,7 +729,7 @@ class Decimal(object):
if other is NotImplemented: if other is NotImplemented:
return other return other
#compare(NaN, NaN) = NaN # Compare(NaN, NaN) = NaN
if (self._is_special or other and other._is_special): if (self._is_special or other and other._is_special):
ans = self._check_nans(other, context) ans = self._check_nans(other, context)
if ans: if ans:
...@@ -780,11 +790,11 @@ class Decimal(object): ...@@ -780,11 +790,11 @@ class Decimal(object):
tmp = map(str, self._int) tmp = map(str, self._int)
numdigits = len(self._int) numdigits = len(self._int)
leftdigits = self._exp + numdigits leftdigits = self._exp + numdigits
if eng and not self: #self = 0eX wants 0[.0[0]]eY, not [[0]0]0eY if eng and not self: # self = 0eX wants 0[.0[0]]eY, not [[0]0]0eY
if self._exp < 0 and self._exp >= -6: #short, no need for e/E if self._exp < 0 and self._exp >= -6: # short, no need for e/E
s = '-'*self._sign + '0.' + '0'*(abs(self._exp)) s = '-'*self._sign + '0.' + '0'*(abs(self._exp))
return s return s
#exp is closest mult. of 3 >= self._exp # exp is closest mult. of 3 >= self._exp
exp = ((self._exp - 1)// 3 + 1) * 3 exp = ((self._exp - 1)// 3 + 1) * 3
if exp != self._exp: if exp != self._exp:
s = '0.'+'0'*(exp - self._exp) s = '0.'+'0'*(exp - self._exp)
...@@ -796,7 +806,7 @@ class Decimal(object): ...@@ -796,7 +806,7 @@ class Decimal(object):
else: else:
s += 'e' s += 'e'
if exp > 0: if exp > 0:
s += '+' #0.0e+3, not 0.0e3 s += '+' # 0.0e+3, not 0.0e3
s += str(exp) s += str(exp)
s = '-'*self._sign + s s = '-'*self._sign + s
return s return s
...@@ -936,19 +946,19 @@ class Decimal(object): ...@@ -936,19 +946,19 @@ class Decimal(object):
return ans return ans
if self._isinfinity(): if self._isinfinity():
#If both INF, same sign => same as both, opposite => error. # If both INF, same sign => same as both, opposite => error.
if self._sign != other._sign and other._isinfinity(): if self._sign != other._sign and other._isinfinity():
return context._raise_error(InvalidOperation, '-INF + INF') return context._raise_error(InvalidOperation, '-INF + INF')
return Decimal(self) return Decimal(self)
if other._isinfinity(): if other._isinfinity():
return Decimal(other) #Can't both be infinity here return Decimal(other) # Can't both be infinity here
shouldround = context._rounding_decision == ALWAYS_ROUND shouldround = context._rounding_decision == ALWAYS_ROUND
exp = min(self._exp, other._exp) exp = min(self._exp, other._exp)
negativezero = 0 negativezero = 0
if context.rounding == ROUND_FLOOR and self._sign != other._sign: if context.rounding == ROUND_FLOOR and self._sign != other._sign:
#If the answer is 0, the sign should be negative, in this case. # If the answer is 0, the sign should be negative, in this case.
negativezero = 1 negativezero = 1
if not self and not other: if not self and not other:
...@@ -983,19 +993,19 @@ class Decimal(object): ...@@ -983,19 +993,19 @@ class Decimal(object):
return Decimal((negativezero, (0,), exp)) return Decimal((negativezero, (0,), exp))
if op1.int < op2.int: if op1.int < op2.int:
op1, op2 = op2, op1 op1, op2 = op2, op1
#OK, now abs(op1) > abs(op2) # OK, now abs(op1) > abs(op2)
if op1.sign == 1: if op1.sign == 1:
result.sign = 1 result.sign = 1
op1.sign, op2.sign = op2.sign, op1.sign op1.sign, op2.sign = op2.sign, op1.sign
else: else:
result.sign = 0 result.sign = 0
#So we know the sign, and op1 > 0. # So we know the sign, and op1 > 0.
elif op1.sign == 1: elif op1.sign == 1:
result.sign = 1 result.sign = 1
op1.sign, op2.sign = (0, 0) op1.sign, op2.sign = (0, 0)
else: else:
result.sign = 0 result.sign = 0
#Now, op1 > abs(op2) > 0 # Now, op1 > abs(op2) > 0
if op2.sign == 0: if op2.sign == 0:
result.int = op1.int + op2.int result.int = op1.int + op2.int
...@@ -1052,8 +1062,8 @@ class Decimal(object): ...@@ -1052,8 +1062,8 @@ class Decimal(object):
ans = self._check_nans(context=context) ans = self._check_nans(context=context)
if ans: if ans:
return ans return ans
# Must be infinite, and incrementing makes no difference
return Decimal(self) # Must be infinite, and incrementing makes no difference return Decimal(self)
L = list(self._int) L = list(self._int)
L[-1] += 1 L[-1] += 1
...@@ -1109,7 +1119,7 @@ class Decimal(object): ...@@ -1109,7 +1119,7 @@ class Decimal(object):
if not self or not other: if not self or not other:
ans = Decimal((resultsign, (0,), resultexp)) ans = Decimal((resultsign, (0,), resultexp))
if shouldround: if shouldround:
#Fixing in case the exponent is out of bounds # Fixing in case the exponent is out of bounds
ans = ans._fix(context) ans = ans._fix(context)
return ans return ans
...@@ -1128,7 +1138,7 @@ class Decimal(object): ...@@ -1128,7 +1138,7 @@ class Decimal(object):
op1 = _WorkRep(self) op1 = _WorkRep(self)
op2 = _WorkRep(other) op2 = _WorkRep(other)
ans = Decimal( (resultsign, map(int, str(op1.int * op2.int)), resultexp)) ans = Decimal((resultsign, map(int, str(op1.int * op2.int)), resultexp))
if shouldround: if shouldround:
ans = ans._fix(context) ans = ans._fix(context)
...@@ -1221,12 +1231,11 @@ class Decimal(object): ...@@ -1221,12 +1231,11 @@ class Decimal(object):
sign, 1) sign, 1)
return context._raise_error(DivisionByZero, 'x / 0', sign) return context._raise_error(DivisionByZero, 'x / 0', sign)
#OK, so neither = 0, INF or NaN # OK, so neither = 0, INF or NaN
shouldround = context._rounding_decision == ALWAYS_ROUND shouldround = context._rounding_decision == ALWAYS_ROUND
#If we're dividing into ints, and self < other, stop. # If we're dividing into ints, and self < other, stop.
#self.__abs__(0) does not round. # self.__abs__(0) does not round.
if divmod and (self.__abs__(0, context) < other.__abs__(0, context)): if divmod and (self.__abs__(0, context) < other.__abs__(0, context)):
if divmod == 1 or divmod == 3: if divmod == 1 or divmod == 3:
...@@ -1238,7 +1247,7 @@ class Decimal(object): ...@@ -1238,7 +1247,7 @@ class Decimal(object):
ans2) ans2)
elif divmod == 2: elif divmod == 2:
#Don't round the mod part, if we don't need it. # Don't round the mod part, if we don't need it.
return (Decimal( (sign, (0,), 0) ), Decimal(self)) return (Decimal( (sign, (0,), 0) ), Decimal(self))
op1 = _WorkRep(self) op1 = _WorkRep(self)
...@@ -1287,7 +1296,7 @@ class Decimal(object): ...@@ -1287,7 +1296,7 @@ class Decimal(object):
op1.exp -= 1 op1.exp -= 1
if res.exp == 0 and divmod and op2.int > op1.int: if res.exp == 0 and divmod and op2.int > op1.int:
#Solves an error in precision. Same as a previous block. # Solves an error in precision. Same as a previous block.
if res.int >= prec_limit and shouldround: if res.int >= prec_limit and shouldround:
return context._raise_error(DivisionImpossible) return context._raise_error(DivisionImpossible)
...@@ -1373,7 +1382,7 @@ class Decimal(object): ...@@ -1373,7 +1382,7 @@ class Decimal(object):
# ignored in the calling function. # ignored in the calling function.
context = context._shallow_copy() context = context._shallow_copy()
flags = context._ignore_flags(Rounded, Inexact) flags = context._ignore_flags(Rounded, Inexact)
#keep DivisionImpossible flags # Keep DivisionImpossible flags
(side, r) = self.__divmod__(other, context=context) (side, r) = self.__divmod__(other, context=context)
if r._isnan(): if r._isnan():
...@@ -1396,7 +1405,7 @@ class Decimal(object): ...@@ -1396,7 +1405,7 @@ class Decimal(object):
if r < comparison: if r < comparison:
r._sign, comparison._sign = s1, s2 r._sign, comparison._sign = s1, s2
#Get flags now # Get flags now
self.__divmod__(other, context=context) self.__divmod__(other, context=context)
return r._fix(context) return r._fix(context)
r._sign, comparison._sign = s1, s2 r._sign, comparison._sign = s1, s2
...@@ -1418,7 +1427,8 @@ class Decimal(object): ...@@ -1418,7 +1427,8 @@ class Decimal(object):
if r > comparison or decrease and r == comparison: if r > comparison or decrease and r == comparison:
r._sign, comparison._sign = s1, s2 r._sign, comparison._sign = s1, s2
context.prec += 1 context.prec += 1
if len(side.__add__(Decimal(1), context=context)._int) >= context.prec: numbsquant = len(side.__add__(Decimal(1), context=context)._int)
if numbsquant >= context.prec:
context.prec -= 1 context.prec -= 1
return context._raise_error(DivisionImpossible)[1] return context._raise_error(DivisionImpossible)[1]
context.prec -= 1 context.prec -= 1
...@@ -1453,7 +1463,7 @@ class Decimal(object): ...@@ -1453,7 +1463,7 @@ class Decimal(object):
context = getcontext() context = getcontext()
return context._raise_error(InvalidContext) return context._raise_error(InvalidContext)
elif self._isinfinity(): elif self._isinfinity():
raise OverflowError, "Cannot convert infinity to long" raise OverflowError("Cannot convert infinity to long")
if self._exp >= 0: if self._exp >= 0:
s = ''.join(map(str, self._int)) + '0'*self._exp s = ''.join(map(str, self._int)) + '0'*self._exp
else: else:
...@@ -1507,13 +1517,13 @@ class Decimal(object): ...@@ -1507,13 +1517,13 @@ class Decimal(object):
context._raise_error(Clamped) context._raise_error(Clamped)
return ans return ans
ans = ans._rescale(Etiny, context=context) ans = ans._rescale(Etiny, context=context)
#It isn't zero, and exp < Emin => subnormal # It isn't zero, and exp < Emin => subnormal
context._raise_error(Subnormal) context._raise_error(Subnormal)
if context.flags[Inexact]: if context.flags[Inexact]:
context._raise_error(Underflow) context._raise_error(Underflow)
else: else:
if ans: if ans:
#Only raise subnormal if non-zero. # Only raise subnormal if non-zero.
context._raise_error(Subnormal) context._raise_error(Subnormal)
else: else:
Etop = context.Etop() Etop = context.Etop()
...@@ -1530,7 +1540,8 @@ class Decimal(object): ...@@ -1530,7 +1540,8 @@ class Decimal(object):
return ans return ans
context._raise_error(Inexact) context._raise_error(Inexact)
context._raise_error(Rounded) context._raise_error(Rounded)
return context._raise_error(Overflow, 'above Emax', ans._sign) c = context._raise_error(Overflow, 'above Emax', ans._sign)
return c
return ans return ans
def _round(self, prec=None, rounding=None, context=None): def _round(self, prec=None, rounding=None, context=None):
...@@ -1590,18 +1601,18 @@ class Decimal(object): ...@@ -1590,18 +1601,18 @@ class Decimal(object):
ans = Decimal( (temp._sign, tmp, temp._exp - expdiff)) ans = Decimal( (temp._sign, tmp, temp._exp - expdiff))
return ans return ans
#OK, but maybe all the lost digits are 0. # OK, but maybe all the lost digits are 0.
lostdigits = self._int[expdiff:] lostdigits = self._int[expdiff:]
if lostdigits == (0,) * len(lostdigits): if lostdigits == (0,) * len(lostdigits):
ans = Decimal( (temp._sign, temp._int[:prec], temp._exp - expdiff)) ans = Decimal( (temp._sign, temp._int[:prec], temp._exp - expdiff))
#Rounded, but not Inexact # Rounded, but not Inexact
context._raise_error(Rounded) context._raise_error(Rounded)
return ans return ans
# Okay, let's round and lose data # Okay, let's round and lose data
this_function = getattr(temp, self._pick_rounding_function[rounding]) this_function = getattr(temp, self._pick_rounding_function[rounding])
#Now we've got the rounding function # Now we've got the rounding function
if prec != context.prec: if prec != context.prec:
context = context._shallow_copy() context = context._shallow_copy()
...@@ -1697,7 +1708,7 @@ class Decimal(object): ...@@ -1697,7 +1708,7 @@ class Decimal(object):
context = getcontext() context = getcontext()
if self._is_special or n._is_special or n.adjusted() > 8: if self._is_special or n._is_special or n.adjusted() > 8:
#Because the spot << doesn't work with really big exponents # Because the spot << doesn't work with really big exponents
if n._isinfinity() or n.adjusted() > 8: if n._isinfinity() or n.adjusted() > 8:
return context._raise_error(InvalidOperation, 'x ** INF') return context._raise_error(InvalidOperation, 'x ** INF')
...@@ -1727,10 +1738,9 @@ class Decimal(object): ...@@ -1727,10 +1738,9 @@ class Decimal(object):
return Infsign[sign] return Infsign[sign]
return Decimal( (sign, (0,), 0) ) return Decimal( (sign, (0,), 0) )
#with ludicrously large exponent, just raise an overflow and return inf. # With ludicrously large exponent, just raise an overflow and return inf.
if not modulo and n > 0 and (self._exp + len(self._int) - 1) * n > context.Emax \ if not modulo and n > 0 \
and self: and (self._exp + len(self._int) - 1) * n > context.Emax and self:
tmp = Decimal('inf') tmp = Decimal('inf')
tmp._sign = sign tmp._sign = sign
context._raise_error(Rounded) context._raise_error(Rounded)
...@@ -1749,7 +1759,7 @@ class Decimal(object): ...@@ -1749,7 +1759,7 @@ class Decimal(object):
context = context._shallow_copy() context = context._shallow_copy()
context.prec = firstprec + elength + 1 context.prec = firstprec + elength + 1
if n < 0: if n < 0:
#n is a long now, not Decimal instance # n is a long now, not Decimal instance
n = -n n = -n
mul = Decimal(1).__div__(mul, context=context) mul = Decimal(1).__div__(mul, context=context)
...@@ -1758,7 +1768,7 @@ class Decimal(object): ...@@ -1758,7 +1768,7 @@ class Decimal(object):
spot <<= 1 spot <<= 1
spot >>= 1 spot >>= 1
#Spot is the highest power of 2 less than n # Spot is the highest power of 2 less than n
while spot: while spot:
val = val.__mul__(val, context=context) val = val.__mul__(val, context=context)
if val._isinfinity(): if val._isinfinity():
...@@ -1816,7 +1826,7 @@ class Decimal(object): ...@@ -1816,7 +1826,7 @@ class Decimal(object):
if exp._isinfinity() or self._isinfinity(): if exp._isinfinity() or self._isinfinity():
if exp._isinfinity() and self._isinfinity(): if exp._isinfinity() and self._isinfinity():
return self #if both are inf, it is OK return self # If both are inf, it is OK
if context is None: if context is None:
context = getcontext() context = getcontext()
return context._raise_error(InvalidOperation, return context._raise_error(InvalidOperation,
...@@ -1848,7 +1858,8 @@ class Decimal(object): ...@@ -1848,7 +1858,8 @@ class Decimal(object):
if self._is_special: if self._is_special:
if self._isinfinity(): if self._isinfinity():
return context._raise_error(InvalidOperation, 'rescale with an INF') return context._raise_error(InvalidOperation,
'rescale with an INF')
ans = self._check_nans(context=context) ans = self._check_nans(context=context)
if ans: if ans:
...@@ -1920,13 +1931,13 @@ class Decimal(object): ...@@ -1920,13 +1931,13 @@ class Decimal(object):
return Decimal(self) return Decimal(self)
if not self: if not self:
#exponent = self._exp / 2, using round_down. # exponent = self._exp / 2, using round_down.
#if self._exp < 0: # if self._exp < 0:
# exp = (self._exp+1) // 2 # exp = (self._exp+1) // 2
#else: # else:
exp = (self._exp) // 2 exp = (self._exp) // 2
if self._sign == 1: if self._sign == 1:
#sqrt(-0) = -0 # sqrt(-0) = -0
return Decimal( (1, (0,), exp)) return Decimal( (1, (0,), exp))
else: else:
return Decimal( (0, (0,), exp)) return Decimal( (0, (0,), exp))
...@@ -1960,8 +1971,7 @@ class Decimal(object): ...@@ -1960,8 +1971,7 @@ class Decimal(object):
ans = ans.__add__(tmp.__mul__(Decimal((0, (8,1,9), -3)), ans = ans.__add__(tmp.__mul__(Decimal((0, (8,1,9), -3)),
context=context), context=context) context=context), context=context)
ans._exp -= 1 + tmp.adjusted() // 2 ans._exp -= 1 + tmp.adjusted() // 2
# ans is now a linear approximation.
#ans is now a linear approximation.
Emax, Emin = context.Emax, context.Emin Emax, Emin = context.Emax, context.Emin
context.Emax, context.Emin = DefaultContext.Emax, DefaultContext.Emin context.Emax, context.Emin = DefaultContext.Emax, DefaultContext.Emin
...@@ -1977,12 +1987,12 @@ class Decimal(object): ...@@ -1977,12 +1987,12 @@ class Decimal(object):
if context.prec == maxp: if context.prec == maxp:
break break
#round to the answer's precision-- the only error can be 1 ulp. # Round to the answer's precision-- the only error can be 1 ulp.
context.prec = firstprec context.prec = firstprec
prevexp = ans.adjusted() prevexp = ans.adjusted()
ans = ans._round(context=context) ans = ans._round(context=context)
#Now, check if the other last digits are better. # Now, check if the other last digits are better.
context.prec = firstprec + 1 context.prec = firstprec + 1
# In case we rounded up another digit and we should actually go lower. # In case we rounded up another digit and we should actually go lower.
if prevexp != ans.adjusted(): if prevexp != ans.adjusted():
...@@ -2014,10 +2024,10 @@ class Decimal(object): ...@@ -2014,10 +2024,10 @@ class Decimal(object):
context._raise_error(Rounded) context._raise_error(Rounded)
context._raise_error(Inexact) context._raise_error(Inexact)
else: else:
#Exact answer, so let's set the exponent right. # Exact answer, so let's set the exponent right.
#if self._exp < 0: # if self._exp < 0:
# exp = (self._exp +1)// 2 # exp = (self._exp +1)// 2
#else: # else:
exp = self._exp // 2 exp = self._exp // 2
context.prec += ans._exp - exp context.prec += ans._exp - exp
ans = ans._rescale(exp, context=context) ans = ans._rescale(exp, context=context)
...@@ -2052,13 +2062,13 @@ class Decimal(object): ...@@ -2052,13 +2062,13 @@ class Decimal(object):
ans = self ans = self
c = self.__cmp__(other) c = self.__cmp__(other)
if c == 0: if c == 0:
# if both operands are finite and equal in numerical value # If both operands are finite and equal in numerical value
# then an ordering is applied: # then an ordering is applied:
# #
# if the signs differ then max returns the operand with the # If the signs differ then max returns the operand with the
# positive sign and min returns the operand with the negative sign # positive sign and min returns the operand with the negative sign
# #
# if the signs are the same then the exponent is used to select # If the signs are the same then the exponent is used to select
# the result. # the result.
if self._sign != other._sign: if self._sign != other._sign:
if self._sign: if self._sign:
...@@ -2079,7 +2089,7 @@ class Decimal(object): ...@@ -2079,7 +2089,7 @@ class Decimal(object):
def min(self, other, context=None): def min(self, other, context=None):
"""Returns the smaller value. """Returns the smaller value.
like min(self, other) except if one is not a number, returns Like min(self, other) except if one is not a number, returns
NaN (and signals if one is sNaN). Also rounds. NaN (and signals if one is sNaN). Also rounds.
""" """
other = _convert_other(other) other = _convert_other(other)
...@@ -2087,7 +2097,7 @@ class Decimal(object): ...@@ -2087,7 +2097,7 @@ class Decimal(object):
return other return other
if self._is_special or other._is_special: if self._is_special or other._is_special:
# if one operand is a quiet NaN and the other is number, then the # If one operand is a quiet NaN and the other is number, then the
# number is always returned # number is always returned
sn = self._isnan() sn = self._isnan()
on = other._isnan() on = other._isnan()
...@@ -2101,13 +2111,13 @@ class Decimal(object): ...@@ -2101,13 +2111,13 @@ class Decimal(object):
ans = self ans = self
c = self.__cmp__(other) c = self.__cmp__(other)
if c == 0: if c == 0:
# if both operands are finite and equal in numerical value # If both operands are finite and equal in numerical value
# then an ordering is applied: # then an ordering is applied:
# #
# if the signs differ then max returns the operand with the # If the signs differ then max returns the operand with the
# positive sign and min returns the operand with the negative sign # positive sign and min returns the operand with the negative sign
# #
# if the signs are the same then the exponent is used to select # If the signs are the same then the exponent is used to select
# the result. # the result.
if self._sign != other._sign: if self._sign != other._sign:
if other._sign: if other._sign:
...@@ -2142,11 +2152,11 @@ class Decimal(object): ...@@ -2142,11 +2152,11 @@ class Decimal(object):
"""Return the adjusted exponent of self""" """Return the adjusted exponent of self"""
try: try:
return self._exp + len(self._int) - 1 return self._exp + len(self._int) - 1
#If NaN or Infinity, self._exp is string # If NaN or Infinity, self._exp is string
except TypeError: except TypeError:
return 0 return 0
# support for pickling, copy, and deepcopy # Support for pickling, copy, and deepcopy
def __reduce__(self): def __reduce__(self):
return (self.__class__, (str(self),)) return (self.__class__, (str(self),))
...@@ -2160,19 +2170,20 @@ class Decimal(object): ...@@ -2160,19 +2170,20 @@ class Decimal(object):
return self # My components are also immutable return self # My components are also immutable
return self.__class__(str(self)) return self.__class__(str(self))
##### Context class ########################################### ##### Context class ##########################################################
# Get rounding method function:
# get rounding method function: rounding_functions = [name for name in Decimal.__dict__.keys()
rounding_functions = [name for name in Decimal.__dict__.keys() if name.startswith('_round_')] if name.startswith('_round_')]
for name in rounding_functions: for name in rounding_functions:
#name is like _round_half_even, goes to the global ROUND_HALF_EVEN value. # Name is like _round_half_even, goes to the global ROUND_HALF_EVEN value.
globalname = name[1:].upper() globalname = name[1:].upper()
val = globals()[globalname] val = globals()[globalname]
Decimal._pick_rounding_function[val] = name Decimal._pick_rounding_function[val] = name
del name, val, globalname, rounding_functions del name, val, globalname, rounding_functions
class ContextManager(object): class ContextManager(object):
"""Helper class to simplify Context management. """Helper class to simplify Context management.
...@@ -2197,12 +2208,13 @@ class ContextManager(object): ...@@ -2197,12 +2208,13 @@ class ContextManager(object):
def __exit__(self, t, v, tb): def __exit__(self, t, v, tb):
setcontext(self.saved_context) setcontext(self.saved_context)
class Context(object): class Context(object):
"""Contains the context for a Decimal instance. """Contains the context for a Decimal instance.
Contains: Contains:
prec - precision (for use in rounding, division, square roots..) prec - precision (for use in rounding, division, square roots..)
rounding - rounding type. (how you round) rounding - rounding type (how you round).
_rounding_decision - ALWAYS_ROUND, NEVER_ROUND -- do you round? _rounding_decision - ALWAYS_ROUND, NEVER_ROUND -- do you round?
traps - If traps[exception] = 1, then the exception is traps - If traps[exception] = 1, then the exception is
raised when it is caused. Otherwise, a value is raised when it is caused. Otherwise, a value is
...@@ -2243,9 +2255,13 @@ class Context(object): ...@@ -2243,9 +2255,13 @@ class Context(object):
def __repr__(self): def __repr__(self):
"""Show the current context.""" """Show the current context."""
s = [] s = []
s.append('Context(prec=%(prec)d, rounding=%(rounding)s, Emin=%(Emin)d, Emax=%(Emax)d, capitals=%(capitals)d' % vars(self)) s.append(
s.append('flags=[' + ', '.join([f.__name__ for f, v in self.flags.items() if v]) + ']') 'Context(prec=%(prec)d, rounding=%(rounding)s, Emin=%(Emin)d, Emax=%(Emax)d, capitals=%(capitals)d'
s.append('traps=[' + ', '.join([t.__name__ for t, v in self.traps.items() if v]) + ']') % vars(self))
s.append('flags=[' + ', '.join([f.__name__ for f, v
in self.flags.items() if v]) + ']')
s.append('traps=[' + ', '.join([t.__name__ for t, v
in self.traps.items() if v]) + ']')
return ', '.join(s) + ')' return ', '.join(s) + ')'
def get_manager(self): def get_manager(self):
...@@ -2265,9 +2281,10 @@ class Context(object): ...@@ -2265,9 +2281,10 @@ class Context(object):
def copy(self): def copy(self):
"""Returns a deep copy from self.""" """Returns a deep copy from self."""
nc = Context(self.prec, self.rounding, self.traps.copy(), self.flags.copy(), nc = Context(self.prec, self.rounding, self.traps.copy(),
self._rounding_decision, self.Emin, self.Emax, self.flags.copy(), self._rounding_decision,
self.capitals, self._clamp, self._ignored_flags) self.Emin, self.Emax, self.capitals,
self._clamp, self._ignored_flags)
return nc return nc
__copy__ = copy __copy__ = copy
...@@ -2281,16 +2298,16 @@ class Context(object): ...@@ -2281,16 +2298,16 @@ class Context(object):
""" """
error = _condition_map.get(condition, condition) error = _condition_map.get(condition, condition)
if error in self._ignored_flags: if error in self._ignored_flags:
#Don't touch the flag # Don't touch the flag
return error().handle(self, *args) return error().handle(self, *args)
self.flags[error] += 1 self.flags[error] += 1
if not self.traps[error]: if not self.traps[error]:
#The errors define how to handle themselves. # The errors define how to handle themselves.
return condition().handle(self, *args) return condition().handle(self, *args)
# Errors should only be risked on copies of the context # Errors should only be risked on copies of the context
#self._ignored_flags = [] # self._ignored_flags = []
raise error, explanation raise error, explanation
def _ignore_all_flags(self): def _ignore_all_flags(self):
...@@ -2314,7 +2331,7 @@ class Context(object): ...@@ -2314,7 +2331,7 @@ class Context(object):
def __hash__(self): def __hash__(self):
"""A Context cannot be hashed.""" """A Context cannot be hashed."""
# We inherit object.__hash__, so we must deny this explicitly # We inherit object.__hash__, so we must deny this explicitly
raise TypeError, "Cannot hash a Context." raise TypeError("Cannot hash a Context.")
def Etiny(self): def Etiny(self):
"""Returns Etiny (= Emin - prec + 1)""" """Returns Etiny (= Emin - prec + 1)"""
...@@ -2340,7 +2357,6 @@ class Context(object): ...@@ -2340,7 +2357,6 @@ class Context(object):
This will make it not round for that operation. This will make it not round for that operation.
""" """
rounding = self._rounding_decision rounding = self._rounding_decision
self._rounding_decision = type self._rounding_decision = type
return rounding return rounding
...@@ -2369,7 +2385,7 @@ class Context(object): ...@@ -2369,7 +2385,7 @@ class Context(object):
d = Decimal(num, context=self) d = Decimal(num, context=self)
return d._fix(self) return d._fix(self)
#Methods # Methods
def abs(self, a): def abs(self, a):
"""Returns the absolute value of the operand. """Returns the absolute value of the operand.
...@@ -2528,10 +2544,10 @@ class Context(object): ...@@ -2528,10 +2544,10 @@ class Context(object):
def multiply(self, a, b): def multiply(self, a, b):
"""multiply multiplies two operands. """multiply multiplies two operands.
If either operand is a special value then the general rules apply. If either operand is a special value then the general rules
Otherwise, the operands are multiplied together ('long multiplication'), apply. Otherwise, the operands are multiplied together
resulting in a number which may be as long as the sum of the lengths ('long multiplication'), resulting in a number which may be
of the two operands. as long as the sum of the lengths of the two operands.
>>> ExtendedContext.multiply(Decimal('1.20'), Decimal('3')) >>> ExtendedContext.multiply(Decimal('1.20'), Decimal('3'))
Decimal("3.60") Decimal("3.60")
...@@ -2592,8 +2608,8 @@ class Context(object): ...@@ -2592,8 +2608,8 @@ class Context(object):
1) before use. 1) before use.
If the increased precision needed for the intermediate calculations If the increased precision needed for the intermediate calculations
exceeds the capabilities of the implementation then an Invalid operation exceeds the capabilities of the implementation then an Invalid
condition is raised. operation condition is raised.
If, when raising to a negative power, an underflow occurs during the If, when raising to a negative power, an underflow occurs during the
division into 1, the operation is not halted at that point but division into 1, the operation is not halted at that point but
...@@ -2631,7 +2647,7 @@ class Context(object): ...@@ -2631,7 +2647,7 @@ class Context(object):
return a.__pow__(b, modulo, context=self) return a.__pow__(b, modulo, context=self)
def quantize(self, a, b): def quantize(self, a, b):
"""Returns a value equal to 'a' (rounded) and having the exponent of 'b'. """Returns a value equal to 'a' (rounded), having the exponent of 'b'.
The coefficient of the result is derived from that of the left-hand The coefficient of the result is derived from that of the left-hand
operand. It may be rounded using the current rounding setting (if the operand. It may be rounded using the current rounding setting (if the
...@@ -2641,8 +2657,8 @@ class Context(object): ...@@ -2641,8 +2657,8 @@ class Context(object):
Unlike other operations, if the length of the coefficient after the Unlike other operations, if the length of the coefficient after the
quantize operation would be greater than precision then an Invalid quantize operation would be greater than precision then an Invalid
operation condition is raised. This guarantees that, unless there is an operation condition is raised. This guarantees that, unless there is
error condition, the exponent of the result of a quantize is always an error condition, the exponent of the result of a quantize is always
equal to that of the right-hand operand. equal to that of the right-hand operand.
Also unlike other operations, quantize will never raise Underflow, even Also unlike other operations, quantize will never raise Underflow, even
...@@ -2685,9 +2701,9 @@ class Context(object): ...@@ -2685,9 +2701,9 @@ class Context(object):
"""Returns the remainder from integer division. """Returns the remainder from integer division.
The result is the residue of the dividend after the operation of The result is the residue of the dividend after the operation of
calculating integer division as described for divide-integer, rounded to calculating integer division as described for divide-integer, rounded
precision digits if necessary. The sign of the result, if non-zero, is to precision digits if necessary. The sign of the result, if non-zero,
the same as that of the original dividend. is the same as that of the original dividend.
This operation will fail under the same conditions as integer division This operation will fail under the same conditions as integer division
(that is, if integer division on the same two operands would fail, the (that is, if integer division on the same two operands would fail, the
...@@ -2753,7 +2769,7 @@ class Context(object): ...@@ -2753,7 +2769,7 @@ class Context(object):
return a.same_quantum(b) return a.same_quantum(b)
def sqrt(self, a): def sqrt(self, a):
"""Returns the square root of a non-negative number to context precision. """Square root of a non-negative number to context precision.
If the result must be inexact, it is rounded using the round-half-even If the result must be inexact, it is rounded using the round-half-even
algorithm. algorithm.
...@@ -2835,6 +2851,7 @@ class Context(object): ...@@ -2835,6 +2851,7 @@ class Context(object):
""" """
return a.to_integral(context=self) return a.to_integral(context=self)
class _WorkRep(object): class _WorkRep(object):
__slots__ = ('sign','int','exp') __slots__ = ('sign','int','exp')
# sign: 0 or 1 # sign: 0 or 1
...@@ -2889,9 +2906,9 @@ def _normalize(op1, op2, shouldround = 0, prec = 0): ...@@ -2889,9 +2906,9 @@ def _normalize(op1, op2, shouldround = 0, prec = 0):
other_len = len(str(other.int)) other_len = len(str(other.int))
if numdigits > (other_len + prec + 1 - tmp_len): if numdigits > (other_len + prec + 1 - tmp_len):
# If the difference in adjusted exps is > prec+1, we know # If the difference in adjusted exps is > prec+1, we know
# other is insignificant, so might as well put a 1 after the precision. # other is insignificant, so might as well put a 1 after the
# (since this is only for addition.) Also stops use of massive longs. # precision (since this is only for addition). Also stops
# use of massive longs.
extend = prec + 2 - tmp_len extend = prec + 2 - tmp_len
if extend <= 0: if extend <= 0:
extend = 1 extend = 1
...@@ -2913,13 +2930,13 @@ def _adjust_coefficients(op1, op2): ...@@ -2913,13 +2930,13 @@ def _adjust_coefficients(op1, op2):
Used on _WorkRep instances during division. Used on _WorkRep instances during division.
""" """
adjust = 0 adjust = 0
#If op1 is smaller, make it larger # If op1 is smaller, make it larger
while op2.int > op1.int: while op2.int > op1.int:
op1.int *= 10 op1.int *= 10
op1.exp -= 1 op1.exp -= 1
adjust += 1 adjust += 1
#If op2 is too small, make it larger # If op2 is too small, make it larger
while op1.int >= (10 * op2.int): while op1.int >= (10 * op2.int):
op2.int *= 10 op2.int *= 10
op2.exp -= 1 op2.exp -= 1
...@@ -2927,7 +2944,8 @@ def _adjust_coefficients(op1, op2): ...@@ -2927,7 +2944,8 @@ def _adjust_coefficients(op1, op2):
return op1, op2, adjust return op1, op2, adjust
##### Helper Functions ########################################
##### Helper Functions #######################################################
def _convert_other(other): def _convert_other(other):
"""Convert other to Decimal. """Convert other to Decimal.
...@@ -2968,16 +2986,16 @@ def _isnan(num): ...@@ -2968,16 +2986,16 @@ def _isnan(num):
if not num: if not num:
return 0 return 0
#get the sign, get rid of trailing [+-] # get the sign, get rid of trailing [+-]
sign = 0 sign = 0
if num[0] == '+': if num[0] == '+':
num = num[1:] num = num[1:]
elif num[0] == '-': #elif avoids '+-nan' elif num[0] == '-': # elif avoids '+-nan'
num = num[1:] num = num[1:]
sign = 1 sign = 1
if num.startswith('nan'): if num.startswith('nan'):
if len(num) > 3 and not num[3:].isdigit(): #diagnostic info if len(num) > 3 and not num[3:].isdigit(): # diagnostic info
return 0 return 0
return (1, sign, num[3:].lstrip('0')) return (1, sign, num[3:].lstrip('0'))
if num.startswith('snan'): if num.startswith('snan'):
...@@ -2987,7 +3005,7 @@ def _isnan(num): ...@@ -2987,7 +3005,7 @@ def _isnan(num):
return 0 return 0
##### Setup Specific Contexts ################################ ##### Setup Specific Contexts ################################################
# The default context prototype used by Context() # The default context prototype used by Context()
# Is mutable, so that new contexts can have different default values # Is mutable, so that new contexts can have different default values
...@@ -3020,19 +3038,19 @@ ExtendedContext = Context( ...@@ -3020,19 +3038,19 @@ ExtendedContext = Context(
) )
##### Useful Constants (internal use only) #################### ##### Useful Constants (internal use only) ###################################
#Reusable defaults # Reusable defaults
Inf = Decimal('Inf') Inf = Decimal('Inf')
negInf = Decimal('-Inf') negInf = Decimal('-Inf')
#Infsign[sign] is infinity w/ that sign # Infsign[sign] is infinity w/ that sign
Infsign = (Inf, negInf) Infsign = (Inf, negInf)
NaN = Decimal('NaN') NaN = Decimal('NaN')
##### crud for parsing strings ################################# ##### crud for parsing strings ################################################
import re import re
# There's an optional sign at the start, and an optional exponent # There's an optional sign at the start, and an optional exponent
...@@ -3052,13 +3070,16 @@ _parser = re.compile(r""" ...@@ -3052,13 +3070,16 @@ _parser = re.compile(r"""
([eE](?P<exp>[-+]? \d+))? ([eE](?P<exp>[-+]? \d+))?
# \s* # \s*
$ $
""", re.VERBOSE).match #Uncomment the \s* to allow leading or trailing spaces. """, re.VERBOSE).match # Uncomment the \s* to allow leading/trailing spaces
del re del re
# return sign, n, p s.t. float string value == -1**sign * n * 10**p exactly
def _string2exact(s): def _string2exact(s):
"""Return sign, n, p s.t.
Float string value == -1**sign * n * 10**p exactly
"""
m = _parser(s) m = _parser(s)
if m is None: if m is None:
raise ValueError("invalid literal for Decimal: %r" % s) raise ValueError("invalid literal for Decimal: %r" % s)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment