# Copyright (c) 2004 Python Software Foundation.# All rights reserved. # Written by Eric Price <eprice at tjhsst.edu># and Facundo Batista <facundo at taniquetil.com.ar># and Raymond Hettinger <python at rcn.com># and Aahz <aahz at pobox.com># and Tim Peters # This module is currently Py2.3 compatible and should be kept that way# unless a major compelling advantage arises. IOW, 2.3 compatibility is# strongly preferred, but not guaranteed. # Also, this module should be kept in sync with the latest updates of# the IBM specification as it evolves. Those updates will be treated# as bug fixes (deviation from the spec is a compatibility, usability# bug) and will be backported. At this point the spec is stabilizing# and the updates are becoming fewer, smaller, and less significant. """This is a Py2.3 implementation of decimal floating point arithmetic based onthe General Decimal Arithmetic Specification: www2.hursley.ibm.com/decimal/decarith.html and IEEE standard 854-1987: www.cs.berkeley.edu/~ejr/projects/754/private/drafts/854-1987/dir.html Decimal floating point has finite precision with arbitrarily large bounds. The purpose of the module is to support arithmetic using familiar"schoolhouse" rules and to avoid the some of tricky representationissues associated with binary floating point. The package is especiallyuseful for financial applications or for contexts where users haveexpectations that are at odds with binary floating point (for instance,in binary floating point, 1.00 % 0.1 gives 0.09999999999999995 insteadof the expected Decimal("0.00") returned by decimal floating point). Here are some examples of using the decimal module: >>> from decimal import *>>> setcontext(ExtendedContext)>>> Decimal(0)Decimal("0")>>> Decimal("1")Decimal("1")>>> Decimal("-.0123")Decimal("-0.0123")>>> Decimal(123456)Decimal("123456")>>> Decimal("123.45e12345678901234567890")Decimal("1.2345E+12345678901234567892")>>> Decimal("1.33") + Decimal("1.27")Decimal("2.60")>>> Decimal("12.34") + Decimal("3.87") - Decimal("18.41")Decimal("-2.20")>>> dig = Decimal(1)>>> print dig / Decimal(3)0.333333333>>> getcontext().prec = 18>>> print dig / Decimal(3)0.333333333333333333>>> print dig.sqrt()1>>> print Decimal(3).sqrt()1.73205080756887729>>> print Decimal(3) ** 1234.85192780976896427E+58>>> inf = Decimal(1) / Decimal(0)>>> print infInfinity>>> neginf = Decimal(-1) / Decimal(0)>>> print neginf-Infinity>>> print neginf + infNaN>>> print neginf * inf-Infinity>>> print dig / 0Infinity>>> getcontext().traps[DivisionByZero] = 1>>> print dig / 0Traceback (most recent call last): ... ... ...DivisionByZero: x / 0>>> c = Context()>>> c.traps[InvalidOperation] = 0>>> print c.flags[InvalidOperation]0>>> c.divide(Decimal(0), Decimal(0))Decimal("NaN")>>> c.traps[InvalidOperation] = 1>>> print c.flags[InvalidOperation]1>>> c.flags[InvalidOperation] = 0>>> print c.flags[InvalidOperation]0>>> print c.divide(Decimal(0), Decimal(0))Traceback (most recent call last): ... ... ...InvalidOperation: 0 / 0>>> print c.flags[InvalidOperation]1>>> c.flags[InvalidOperation] = 0>>> c.traps[InvalidOperation] = 0>>> print c.divide(Decimal(0), Decimal(0))NaN>>> print c.flags[InvalidOperation]1>>>""" __all__ = [ # Two major classes 'Decimal', 'Context', # Contexts 'DefaultContext', 'BasicContext', 'ExtendedContext', # Exceptions 'DecimalException', 'Clamped', 'InvalidOperation', 'DivisionByZero', 'Inexact', 'Rounded', 'Subnormal', 'Overflow', 'Underflow', # Constants for use in setting up contexts 'ROUND_DOWN', 'ROUND_HALF_UP', 'ROUND_HALF_EVEN', 'ROUND_CEILING', 'ROUND_FLOOR', 'ROUND_UP', 'ROUND_HALF_DOWN', # Functions for manipulating contexts 'setcontext', 'getcontext', 'localcontext'] import copy as _copy #RoundingROUND_DOWN = 'ROUND_DOWN'ROUND_HALF_UP = 'ROUND_HALF_UP'ROUND_HALF_EVEN = 'ROUND_HALF_EVEN'ROUND_CEILING = 'ROUND_CEILING'ROUND_FLOOR = 'ROUND_FLOOR'ROUND_UP = 'ROUND_UP'ROUND_HALF_DOWN = 'ROUND_HALF_DOWN' #Rounding decision (not part of the public API)NEVER_ROUND = 'NEVER_ROUND' # Round in division (non-divmod), sqrt ONLYALWAYS_ROUND = 'ALWAYS_ROUND' # Every operation rounds at end. #Errors class DecimalException(ArithmeticError): """Base exception class. Used exceptions derive from this. If an exception derives from another exception besides this (such as Underflow (Inexact, Rounded, Subnormal) that indicates that it is only called if the others are present. This isn't actually used for 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 from DecimalException. """ def handle(self, context, *args): pass class Clamped(DecimalException): """Exponent of a 0 changed to fit bounds. This occurs and signals clamped if the exponent of a result has been altered in order to fit the constraints of a specific concrete representation. This may occur when the exponent of a zero result would be outside the bounds of a representation, or when a large normal number would have an encoded exponent that cannot be represented. In this latter case, the exponent is reduced to fit and the corresponding number of zero digits are appended to the coefficient ("fold-down"). """ class InvalidOperation(DecimalException): """An invalid operation was performed. Various bad things cause this: Something creates a signaling NaN -INF + INF 0 * (+-)INF (+-)INF / (+-)INF x % 0 (+-)INF % x x._rescale( non-integer ) sqrt(-x) , x > 0 0 ** 0 x ** (non-integer) x ** (+-)INF An operand is invalid """ def handle(self, context, *args): if args: if args[0] == 1: #sNaN, must drop 's' but keep diagnostics return Decimal( (args[1]._sign, args[1]._int, 'n') ) return NaN class ConversionSyntax(InvalidOperation): """Trying to convert badly formed string. This occurs and signals invalid-operation if an string is being converted to a number and it does not conform to the numeric string syntax. The result is [0,qNaN]. """ def handle(self, context, *args): return (0, (0,), 'n') #Passed to something which uses a tuple. class DivisionByZero(DecimalException, ZeroDivisionError): """Division by 0. This occurs and signals division-by-zero if division of a finite number by zero was attempted (during a divide-integer or divide operation, or a power operation with negative right-hand operand), and the dividend was not zero. The result of the operation is [sign,inf], where sign is the exclusive or of the signs of the operands for divide, or is 1 for an odd power of -0, for power. """ def handle(self, context, sign, double = None, *args): if double is not None: return (Infsign[sign],)*2 return Infsign[sign] class DivisionImpossible(InvalidOperation): """Cannot perform the division adequately. This occurs and signals invalid-operation if the integer result of a divide-integer or remainder operation had too many digits (would be longer than precision). The result is [0,qNaN]. """ def handle(self, context, *args): return (NaN, NaN) class DivisionUndefined(InvalidOperation, ZeroDivisionError): """Undefined result of division. This occurs and signals invalid-operation if division by zero was attempted (during a divide-integer, divide, or remainder operation), and the dividend is also zero. The result is [0,qNaN]. """ def handle(self, context, tup=None, *args): if tup is not None: return (NaN, NaN) #for 0 %0, 0 // 0 return NaN class Inexact(DecimalException): """Had to round, losing information. This occurs and signals inexact whenever the result of an operation is not exact (that is, it needed to be rounded and any discarded digits were non-zero), or if an overflow or underflow condition occurs. The result in all cases is unchanged. The inexact signal may be tested (or trapped) to determine if a given operation (or sequence of operations) was inexact. """ pass class InvalidContext(InvalidOperation): """Invalid context. Unknown rounding, for example. This occurs and signals invalid-operation if an invalid context was detected during an operation. This can occur if contexts are not checked on creation and either the precision exceeds the capability of the underlying concrete representation or an unknown or unsupported rounding was specified. These aspects of the context need only be checked when the values are required to be used. The result is [0,qNaN]. """ def handle(self, context, *args): return NaN class Rounded(DecimalException): """Number got rounded (not necessarily changed during rounding). This occurs and signals rounded whenever the result of an operation is rounded (that is, some zero or non-zero digits were discarded from the coefficient), or if an overflow or underflow condition occurs. The result in all cases is unchanged. The rounded signal may be tested (or trapped) to determine if a given operation (or sequence of operations) caused a loss of precision. """ pass class Subnormal(DecimalException): """Exponent < Emin before rounding. This occurs and signals subnormal whenever the result of a conversion or operation is subnormal (that is, its adjusted exponent is less than Emin, before any rounding). The result in all cases is unchanged. The subnormal signal may be tested (or trapped) to determine if a given or operation (or sequence of operations) yielded a subnormal result. """ pass class Overflow(Inexact, Rounded): """Numerical overflow. This occurs and signals overflow if the adjusted exponent of a result (from a conversion or from an operation that is not an attempt to divide by zero), after rounding, would be greater than the largest value that can be handled by the implementation (the value Emax). The result depends on the rounding mode: For round-half-up and round-half-even (and for round-half-down and round-up, if implemented), the result of the operation is [sign,inf], where sign is the sign of the intermediate result. For round-down, the result is the largest finite number that can be represented in the current precision, with the sign of the intermediate result. For round-ceiling, the result is the same as for round-down if the sign of the intermediate result is 1, or is [0,inf] otherwise. For round-floor, the result is the same as for round-down if the sign of the intermediate result is 0, or is [1,inf] otherwise. In all cases, Inexact and Rounded will also be raised. """ def handle(self, context, sign, *args): if context.rounding in (ROUND_HALF_UP, ROUND_HALF_EVEN, ROUND_HALF_DOWN, ROUND_UP): return Infsign[sign] if sign == 0: if context.rounding == ROUND_CEILING: return Infsign[sign] return Decimal((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)) class Underflow(Inexact, Rounded, Subnormal): """Numerical underflow with result rounded to 0. This occurs and signals underflow if a result is inexact and the adjusted exponent of the result would be smaller (more negative) than the smallest value that can be handled by the implementation (the value Emin). That is, the result is both inexact and subnormal. The result after an underflow will be a subnormal number rounded, if necessary, so that its exponent is not less than Etiny. This may result in 0 with the sign of the intermediate result and an exponent of Etiny. In all cases, Inexact, Rounded, and Subnormal will also be raised. """ # List of public traps and flags_signals = [Clamped, DivisionByZero, Inexact, Overflow, Rounded, Underflow, InvalidOperation, Subnormal] # Map conditions (per the spec) to signals_condition_map = {ConversionSyntax:InvalidOperation, DivisionImpossible:InvalidOperation, DivisionUndefined:InvalidOperation, InvalidContext:InvalidOperation} ##### Context Functions ####################################### # The getcontext() and setcontext() function manage access to a thread-local# current context. Py2.4 offers direct support for thread locals. If that# 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# mock threading object with threading.local() returning the module namespace. try: import threadingexcept ImportError: # Python was compiled without threads; create a mock object instead import sys class MockThreading: def local(self, sys=sys): return sys.modules[__name__] threading = MockThreading() del sys, MockThreading try: threading.local except AttributeError: #To fix reloading, force it to create a new context #Old contexts have different exceptions in their dicts, making problems. if hasattr(threading.currentThread(), '__decimal_context__'): del threading.currentThread().__decimal_context__ def setcontext(context): """Set this thread's context to context.""" if context in (DefaultContext, BasicContext, ExtendedContext): context = context.copy() context.clear_flags() threading.currentThread().__decimal_context__ = context def getcontext(): """Returns this thread's context. If this thread does not yet have a context, returns a new context and sets this thread's context. New contexts are copies of DefaultContext. """ try: return threading.currentThread().__decimal_context__ except AttributeError: context = Context() threading.currentThread().__decimal_context__ = context return context else: local = threading.local() if hasattr(local, '__decimal_context__'): del local.__decimal_context__ def getcontext(_local=local): """Returns this thread's context. If this thread does not yet have a context, returns a new context and sets this thread's context. New contexts are copies of DefaultContext. """ try: return _local.__decimal_context__ except AttributeError: context = Context() _local.__decimal_context__ = context return context def setcontext(context, _local=local): """Set this thread's context to context.""" if context in (DefaultContext, BasicContext, ExtendedContext): context = context.copy() context.clear_flags() _local.__decimal_context__ = context del threading, local # Don't contaminate the namespace def localcontext(ctx=None): """Return a context manager for a copy of the supplied context Uses a copy of the current context if no context is specified The returned context manager creates a local decimal context in a with statement: def sin(x): with localcontext() as ctx: ctx.prec += 2 # Rest of sin calculation algorithm # uses a precision 2 greater than normal return +s # Convert result to normal precision def sin(x): with localcontext(ExtendedContext): # Rest of sin calculation algorithm # uses the Extended Context from the # General Decimal Arithmetic Specification return +s # Convert result to normal context """ # The string below can't be included in the docstring until Python 2.6 # as the doctest module doesn't understand __future__ statements """ >>> from __future__ import with_statement >>> print getcontext().prec 28 >>> with localcontext(): ... ctx = getcontext() ... ctx.prec += 2 ... print ctx.prec ... 30 >>> with localcontext(ExtendedContext): ... print getcontext().prec ... 9 >>> print getcontext().prec 28 """Lines 1–500 of 3137. Load more
# Copyright (c) 2004 Python Software Foundation. # All rights reserved. # Written by Eric Price <eprice at tjhsst.edu> # and Facundo Batista <facundo at taniquetil.com.ar> # and Raymond Hettinger <python at rcn.com> # and Aahz <aahz at pobox.com> # and Tim Peters # This module is currently Py2.3 compatible and should be kept that way # unless a major compelling advantage arises. IOW, 2.3 compatibility is # strongly preferred, but not guaranteed. # Also, this module should be kept in sync with the latest updates of # the IBM specification as it evolves. Those updates will be treated # as bug fixes (deviation from the spec is a compatibility, usability # bug) and will be backported. At this point the spec is stabilizing # and the updates are becoming fewer, smaller, and less significant. """ This is a Py2.3 implementation of decimal floating point arithmetic based on the General Decimal Arithmetic Specification: www2.hursley.ibm.com/decimal/decarith.html and IEEE standard 854-1987: www.cs.berkeley.edu/~ejr/projects/754/private/drafts/854-1987/dir.html Decimal floating point has finite precision with arbitrarily large bounds. The purpose of the module is to support arithmetic using familiar "schoolhouse" rules and to avoid the some of tricky representation issues associated with binary floating point. The package is especially useful for financial applications or for contexts where users have expectations that are at odds with binary floating point (for instance, in binary floating point, 1.00 % 0.1 gives 0.09999999999999995 instead of the expected Decimal("0.00") returned by decimal floating point). Here are some examples of using the decimal module: >>> from decimal import * >>> setcontext(ExtendedContext) >>> Decimal(0) Decimal("0") >>> Decimal("1") Decimal("1") >>> Decimal("-.0123") Decimal("-0.0123") >>> Decimal(123456) Decimal("123456") >>> Decimal("123.45e12345678901234567890") Decimal("1.2345E+12345678901234567892") >>> Decimal("1.33") + Decimal("1.27") Decimal("2.60") >>> Decimal("12.34") + Decimal("3.87") - Decimal("18.41") Decimal("-2.20") >>> dig = Decimal(1) >>> print dig / Decimal(3) 0.333333333 >>> getcontext().prec = 18 >>> print dig / Decimal(3) 0.333333333333333333 >>> print dig.sqrt() 1 >>> print Decimal(3).sqrt() 1.73205080756887729 >>> print Decimal(3) ** 123 4.85192780976896427E+58 >>> inf = Decimal(1) / Decimal(0) >>> print inf Infinity >>> neginf = Decimal(-1) / Decimal(0) >>> print neginf -Infinity >>> print neginf + inf NaN >>> print neginf * inf -Infinity >>> print dig / 0 Infinity >>> getcontext().traps[DivisionByZero] = 1 >>> print dig / 0 Traceback (most recent call last): ... ... ... DivisionByZero: x / 0 >>> c = Context() >>> c.traps[InvalidOperation] = 0 >>> print c.flags[InvalidOperation] 0 >>> c.divide(Decimal(0), Decimal(0)) Decimal("NaN") >>> c.traps[InvalidOperation] = 1 >>> print c.flags[InvalidOperation] 1 >>> c.flags[InvalidOperation] = 0 >>> print c.flags[InvalidOperation] 0 >>> print c.divide(Decimal(0), Decimal(0)) Traceback (most recent call last): ... ... ... InvalidOperation: 0 / 0 >>> print c.flags[InvalidOperation] 1 >>> c.flags[InvalidOperation] = 0 >>> c.traps[InvalidOperation] = 0 >>> print c.divide(Decimal(0), Decimal(0)) NaN >>> print c.flags[InvalidOperation] 1 >>> """ __all__ = [ # Two major classes 'Decimal', 'Context', # Contexts 'DefaultContext', 'BasicContext', 'ExtendedContext', # Exceptions 'DecimalException', 'Clamped', 'InvalidOperation', 'DivisionByZero', 'Inexact', 'Rounded', 'Subnormal', 'Overflow', 'Underflow', # Constants for use in setting up contexts 'ROUND_DOWN', 'ROUND_HALF_UP', 'ROUND_HALF_EVEN', 'ROUND_CEILING', 'ROUND_FLOOR', 'ROUND_UP', 'ROUND_HALF_DOWN', # Functions for manipulating contexts 'setcontext', 'getcontext', 'localcontext' ] import copy as _copy #Rounding ROUND_DOWN = 'ROUND_DOWN' ROUND_HALF_UP = 'ROUND_HALF_UP' ROUND_HALF_EVEN = 'ROUND_HALF_EVEN' ROUND_CEILING = 'ROUND_CEILING' ROUND_FLOOR = 'ROUND_FLOOR' ROUND_UP = 'ROUND_UP' ROUND_HALF_DOWN = 'ROUND_HALF_DOWN' #Rounding decision (not part of the public API) NEVER_ROUND = 'NEVER_ROUND' # Round in division (non-divmod), sqrt ONLY ALWAYS_ROUND = 'ALWAYS_ROUND' # Every operation rounds at end. #Errors class DecimalException(ArithmeticError): """Base exception class. Used exceptions derive from this. If an exception derives from another exception besides this (such as Underflow (Inexact, Rounded, Subnormal) that indicates that it is only called if the others are present. This isn't actually used for 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 from DecimalException. """ def handle(self, context, *args): pass class Clamped(DecimalException): """Exponent of a 0 changed to fit bounds. This occurs and signals clamped if the exponent of a result has been altered in order to fit the constraints of a specific concrete representation. This may occur when the exponent of a zero result would be outside the bounds of a representation, or when a large normal number would have an encoded exponent that cannot be represented. In this latter case, the exponent is reduced to fit and the corresponding number of zero digits are appended to the coefficient ("fold-down"). """ class InvalidOperation(DecimalException): """An invalid operation was performed. Various bad things cause this: Something creates a signaling NaN -INF + INF 0 * (+-)INF (+-)INF / (+-)INF x % 0 (+-)INF % x x._rescale( non-integer ) sqrt(-x) , x > 0 0 ** 0 x ** (non-integer) x ** (+-)INF An operand is invalid """ def handle(self, context, *args): if args: if args[0] == 1: #sNaN, must drop 's' but keep diagnostics return Decimal( (args[1]._sign, args[1]._int, 'n') ) return NaN class ConversionSyntax(InvalidOperation): """Trying to convert badly formed string. This occurs and signals invalid-operation if an string is being converted to a number and it does not conform to the numeric string syntax. The result is [0,qNaN]. """ def handle(self, context, *args): return (0, (0,), 'n') #Passed to something which uses a tuple. class DivisionByZero(DecimalException, ZeroDivisionError): """Division by 0. This occurs and signals division-by-zero if division of a finite number by zero was attempted (during a divide-integer or divide operation, or a power operation with negative right-hand operand), and the dividend was not zero. The result of the operation is [sign,inf], where sign is the exclusive or of the signs of the operands for divide, or is 1 for an odd power of -0, for power. """ def handle(self, context, sign, double = None, *args): if double is not None: return (Infsign[sign],)*2 return Infsign[sign] class DivisionImpossible(InvalidOperation): """Cannot perform the division adequately. This occurs and signals invalid-operation if the integer result of a divide-integer or remainder operation had too many digits (would be longer than precision). The result is [0,qNaN]. """ def handle(self, context, *args): return (NaN, NaN) class DivisionUndefined(InvalidOperation, ZeroDivisionError): """Undefined result of division. This occurs and signals invalid-operation if division by zero was attempted (during a divide-integer, divide, or remainder operation), and the dividend is also zero. The result is [0,qNaN]. """ def handle(self, context, tup=None, *args): if tup is not None: return (NaN, NaN) #for 0 %0, 0 // 0 return NaN class Inexact(DecimalException): """Had to round, losing information. This occurs and signals inexact whenever the result of an operation is not exact (that is, it needed to be rounded and any discarded digits were non-zero), or if an overflow or underflow condition occurs. The result in all cases is unchanged. The inexact signal may be tested (or trapped) to determine if a given operation (or sequence of operations) was inexact. """ pass class InvalidContext(InvalidOperation): """Invalid context. Unknown rounding, for example. This occurs and signals invalid-operation if an invalid context was detected during an operation. This can occur if contexts are not checked on creation and either the precision exceeds the capability of the underlying concrete representation or an unknown or unsupported rounding was specified. These aspects of the context need only be checked when the values are required to be used. The result is [0,qNaN]. """ def handle(self, context, *args): return NaN class Rounded(DecimalException): """Number got rounded (not necessarily changed during rounding). This occurs and signals rounded whenever the result of an operation is rounded (that is, some zero or non-zero digits were discarded from the coefficient), or if an overflow or underflow condition occurs. The result in all cases is unchanged. The rounded signal may be tested (or trapped) to determine if a given operation (or sequence of operations) caused a loss of precision. """ pass class Subnormal(DecimalException): """Exponent < Emin before rounding. This occurs and signals subnormal whenever the result of a conversion or operation is subnormal (that is, its adjusted exponent is less than Emin, before any rounding). The result in all cases is unchanged. The subnormal signal may be tested (or trapped) to determine if a given or operation (or sequence of operations) yielded a subnormal result. """ pass class Overflow(Inexact, Rounded): """Numerical overflow. This occurs and signals overflow if the adjusted exponent of a result (from a conversion or from an operation that is not an attempt to divide by zero), after rounding, would be greater than the largest value that can be handled by the implementation (the value Emax). The result depends on the rounding mode: For round-half-up and round-half-even (and for round-half-down and round-up, if implemented), the result of the operation is [sign,inf], where sign is the sign of the intermediate result. For round-down, the result is the largest finite number that can be represented in the current precision, with the sign of the intermediate result. For round-ceiling, the result is the same as for round-down if the sign of the intermediate result is 1, or is [0,inf] otherwise. For round-floor, the result is the same as for round-down if the sign of the intermediate result is 0, or is [1,inf] otherwise. In all cases, Inexact and Rounded will also be raised. """ def handle(self, context, sign, *args): if context.rounding in (ROUND_HALF_UP, ROUND_HALF_EVEN, ROUND_HALF_DOWN, ROUND_UP): return Infsign[sign] if sign == 0: if context.rounding == ROUND_CEILING: return Infsign[sign] return Decimal((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)) class Underflow(Inexact, Rounded, Subnormal): """Numerical underflow with result rounded to 0. This occurs and signals underflow if a result is inexact and the adjusted exponent of the result would be smaller (more negative) than the smallest value that can be handled by the implementation (the value Emin). That is, the result is both inexact and subnormal. The result after an underflow will be a subnormal number rounded, if necessary, so that its exponent is not less than Etiny. This may result in 0 with the sign of the intermediate result and an exponent of Etiny. In all cases, Inexact, Rounded, and Subnormal will also be raised. """ # List of public traps and flags _signals = [Clamped, DivisionByZero, Inexact, Overflow, Rounded, Underflow, InvalidOperation, Subnormal] # Map conditions (per the spec) to signals _condition_map = {ConversionSyntax:InvalidOperation, DivisionImpossible:InvalidOperation, DivisionUndefined:InvalidOperation, InvalidContext:InvalidOperation} ##### Context Functions ####################################### # The getcontext() and setcontext() function manage access to a thread-local # current context. Py2.4 offers direct support for thread locals. If that # 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 # mock threading object with threading.local() returning the module namespace. try: import threading except ImportError: # Python was compiled without threads; create a mock object instead import sys class MockThreading: def local(self, sys=sys): return sys.modules[__name__] threading = MockThreading() del sys, MockThreading try: threading.local except AttributeError: #To fix reloading, force it to create a new context #Old contexts have different exceptions in their dicts, making problems. if hasattr(threading.currentThread(), '__decimal_context__'): del threading.currentThread().__decimal_context__ def setcontext(context): """Set this thread's context to context.""" if context in (DefaultContext, BasicContext, ExtendedContext): context = context.copy() context.clear_flags() threading.currentThread().__decimal_context__ = context def getcontext(): """Returns this thread's context. If this thread does not yet have a context, returns a new context and sets this thread's context. New contexts are copies of DefaultContext. """ try: return threading.currentThread().__decimal_context__ except AttributeError: context = Context() threading.currentThread().__decimal_context__ = context return context else: local = threading.local() if hasattr(local, '__decimal_context__'): del local.__decimal_context__ def getcontext(_local=local): """Returns this thread's context. If this thread does not yet have a context, returns a new context and sets this thread's context. New contexts are copies of DefaultContext. """ try: return _local.__decimal_context__ except AttributeError: context = Context() _local.__decimal_context__ = context return context def setcontext(context, _local=local): """Set this thread's context to context.""" if context in (DefaultContext, BasicContext, ExtendedContext): context = context.copy() context.clear_flags() _local.__decimal_context__ = context del threading, local # Don't contaminate the namespace def localcontext(ctx=None): """Return a context manager for a copy of the supplied context Uses a copy of the current context if no context is specified The returned context manager creates a local decimal context in a with statement: def sin(x): with localcontext() as ctx: ctx.prec += 2 # Rest of sin calculation algorithm # uses a precision 2 greater than normal return +s # Convert result to normal precision def sin(x): with localcontext(ExtendedContext): # Rest of sin calculation algorithm # uses the Extended Context from the # General Decimal Arithmetic Specification return +s # Convert result to normal context """ # The string below can't be included in the docstring until Python 2.6 # as the doctest module doesn't understand __future__ statements """ >>> from __future__ import with_statement >>> print getcontext().prec 28 >>> with localcontext(): ... ctx = getcontext() ... ctx.prec += 2 ... print ctx.prec ... 30 >>> with localcontext(ExtendedContext): ... print getcontext().prec ... 9 >>> print getcontext().prec 28 """