mirror of
https://github.com/python/cpython.git
synced 2025-07-17 00:05:20 +00:00
Module and tests:
* Map conditions to related signals. * Make contexts unhashable. * Eliminate used "default" attribute in exception definitions. * Eliminate the _filterfunc in favor of a straight list. Docs: * Eliminate documented references to conditions that are not signals. * Eliminate parenthetical notes such as "1/0 --> Inf" which are no longer true with the new defaults.
This commit is contained in:
parent
563e449729
commit
5aa478badf
4 changed files with 45 additions and 88 deletions
|
@ -66,10 +66,8 @@ context for arithmetic, and signals.
|
||||||
A decimal number is immutable. It has a sign, coefficient digits, and an
|
A decimal number is immutable. It has a sign, coefficient digits, and an
|
||||||
exponent. To preserve significance, the coefficient digits do not truncate
|
exponent. To preserve significance, the coefficient digits do not truncate
|
||||||
trailing zeroes. Decimals also include special values such as
|
trailing zeroes. Decimals also include special values such as
|
||||||
\constant{Infinity} (the result of \samp{1 / 0}), \constant{-Infinity},
|
\constant{Infinity}, \constant{-Infinity}, and \constant{NaN}. The standard
|
||||||
(the result of \samp{-1 / 0}), and \constant{NaN} (the result of
|
also differentiates \constant{-0} from \constant{+0}.
|
||||||
\samp{0 / 0}). The standard also differentiates \constant{-0} from
|
|
||||||
\constant{+0}.
|
|
||||||
|
|
||||||
The context for arithmetic is an environment specifying precision, rounding
|
The context for arithmetic is an environment specifying precision, rounding
|
||||||
rules, limits on exponents, flags that indicate the results of operations,
|
rules, limits on exponents, flags that indicate the results of operations,
|
||||||
|
@ -82,9 +80,7 @@ Signals are types of information that arise during the course of a
|
||||||
computation. Depending on the needs of the application, some signals may be
|
computation. Depending on the needs of the application, some signals may be
|
||||||
ignored, considered as informational, or treated as exceptions. The signals in
|
ignored, considered as informational, or treated as exceptions. The signals in
|
||||||
the decimal module are: \constant{Clamped}, \constant{InvalidOperation},
|
the decimal module are: \constant{Clamped}, \constant{InvalidOperation},
|
||||||
\constant{ConversionSyntax}, \constant{DivisionByZero},
|
\constant{DivisionByZero}, \constant{Inexact}, \constant{Rounded},
|
||||||
\constant{DivisionImpossible}, \constant{DivisionUndefined},
|
|
||||||
\constant{Inexact}, \constant{InvalidContext}, \constant{Rounded},
|
|
||||||
\constant{Subnormal}, \constant{Overflow}, and \constant{Underflow}.
|
\constant{Subnormal}, \constant{Overflow}, and \constant{Underflow}.
|
||||||
|
|
||||||
For each signal there is a flag and a trap enabler. When a signal is
|
For each signal there is a flag and a trap enabler. When a signal is
|
||||||
|
@ -124,7 +120,7 @@ Context(prec=28, rounding=ROUND_HALF_EVEN, Emin=-999999999, Emax=999999999,
|
||||||
Decimal instances can be constructed from integers, strings or tuples. To
|
Decimal instances can be constructed from integers, strings or tuples. To
|
||||||
create a Decimal from a \class{float}, first convert it to a string. This
|
create a Decimal from a \class{float}, first convert it to a string. This
|
||||||
serves as an explicit reminder of the details of the conversion (including
|
serves as an explicit reminder of the details of the conversion (including
|
||||||
representation error). Malformed strings signal \constant{ConversionSyntax}
|
representation error). Malformed strings signal \constant{InvalidOperation}
|
||||||
and return a special kind of Decimal called a \constant{NaN} which stands for
|
and return a special kind of Decimal called a \constant{NaN} which stands for
|
||||||
``Not a number''. Positive and negative \constant{Infinity} is yet another
|
``Not a number''. Positive and negative \constant{Infinity} is yet another
|
||||||
special kind of Decimal.
|
special kind of Decimal.
|
||||||
|
@ -274,10 +270,8 @@ To turn all the traps on or off all at once, use a loop. Also, the
|
||||||
>>> getcontext().trap_enablers.update({Rounded:0, Inexact:0, Subnormal:0})
|
>>> getcontext().trap_enablers.update({Rounded:0, Inexact:0, Subnormal:0})
|
||||||
>>> getcontext()
|
>>> getcontext()
|
||||||
Context(prec=9, rounding=ROUND_HALF_EVEN, Emin=-999999999, Emax=999999999,
|
Context(prec=9, rounding=ROUND_HALF_EVEN, Emin=-999999999, Emax=999999999,
|
||||||
setflags=[], settraps=['Underflow', 'DecimalException', 'Clamped',
|
setflags=[], settraps=['Clamped', 'Underflow', 'InvalidOperation',
|
||||||
'InvalidContext', 'InvalidOperation', 'ConversionSyntax',
|
'DivisionByZero', 'Overflow'])
|
||||||
'DivisionByZero', 'DivisionImpossible', 'DivisionUndefined',
|
|
||||||
'Overflow'])
|
|
||||||
\end{verbatim}
|
\end{verbatim}
|
||||||
|
|
||||||
Applications typically set the context once at the beginning of a program
|
Applications typically set the context once at the beginning of a program
|
||||||
|
@ -320,7 +314,7 @@ as other Python numeric types.
|
||||||
|
|
||||||
The supplied \var{context} or, if not specified, the current context
|
The supplied \var{context} or, if not specified, the current context
|
||||||
governs only the handling of malformed strings not conforming to the
|
governs only the handling of malformed strings not conforming to the
|
||||||
numeric string syntax. If the context traps \constant{ConversionSyntax},
|
numeric string syntax. If the context traps \constant{InvalidOperation},
|
||||||
an exception is raised; otherwise, the constructor returns a new Decimal
|
an exception is raised; otherwise, the constructor returns a new Decimal
|
||||||
with the value of \constant{NaN}.
|
with the value of \constant{NaN}.
|
||||||
|
|
||||||
|
@ -730,13 +724,6 @@ exception is raised upon encountering the condition.
|
||||||
reduced to fit by adding zeroes to the coefficient.
|
reduced to fit by adding zeroes to the coefficient.
|
||||||
\end{classdesc*}
|
\end{classdesc*}
|
||||||
|
|
||||||
\begin{classdesc*}{ConversionSyntax}
|
|
||||||
Trying to convert a malformed string such as: \code{Decimal('jump')}.
|
|
||||||
|
|
||||||
Decimal converts only strings conforming to the numeric string
|
|
||||||
syntax. If this signal is not trapped, returns \constant{NaN}.
|
|
||||||
\end{classdesc*}
|
|
||||||
|
|
||||||
\begin{classdesc*}{DecimalException}
|
\begin{classdesc*}{DecimalException}
|
||||||
Base class for other signals.
|
Base class for other signals.
|
||||||
\end{classdesc*}
|
\end{classdesc*}
|
||||||
|
@ -750,19 +737,6 @@ exception is raised upon encountering the condition.
|
||||||
the inputs to the calculation.
|
the inputs to the calculation.
|
||||||
\end{classdesc*}
|
\end{classdesc*}
|
||||||
|
|
||||||
\begin{classdesc*}{DivisionImpossible}
|
|
||||||
Error performing a division operation. Caused when an intermediate result
|
|
||||||
has more digits that the allowed by the current precision. If not trapped,
|
|
||||||
returns \constant{NaN}.
|
|
||||||
\end{classdesc*}
|
|
||||||
|
|
||||||
|
|
||||||
\begin{classdesc*}{DivisionUndefined}
|
|
||||||
This is a subclass of \class{DivisionByZero}.
|
|
||||||
|
|
||||||
It occurs only in the context of division operations.
|
|
||||||
\end{classdesc*}
|
|
||||||
|
|
||||||
\begin{classdesc*}{Inexact}
|
\begin{classdesc*}{Inexact}
|
||||||
Indicates that rounding occurred and the result is not exact.
|
Indicates that rounding occurred and the result is not exact.
|
||||||
|
|
||||||
|
@ -771,14 +745,6 @@ exception is raised upon encountering the condition.
|
||||||
to detect when results are inexact.
|
to detect when results are inexact.
|
||||||
\end{classdesc*}
|
\end{classdesc*}
|
||||||
|
|
||||||
|
|
||||||
\begin{classdesc*}{InvalidContext}
|
|
||||||
This is a subclass of \class{InvalidOperation}.
|
|
||||||
|
|
||||||
Indicates an error within the Context object such as an unknown
|
|
||||||
rounding operation. If not trapped, returns \constant{NaN}.
|
|
||||||
\end{classdesc*}
|
|
||||||
|
|
||||||
\begin{classdesc*}{InvalidOperation}
|
\begin{classdesc*}{InvalidOperation}
|
||||||
An invalid operation was performed.
|
An invalid operation was performed.
|
||||||
|
|
||||||
|
@ -809,7 +775,6 @@ exception is raised upon encountering the condition.
|
||||||
\class{Rounded} are also signaled.
|
\class{Rounded} are also signaled.
|
||||||
\end{classdesc*}
|
\end{classdesc*}
|
||||||
|
|
||||||
|
|
||||||
\begin{classdesc*}{Rounded}
|
\begin{classdesc*}{Rounded}
|
||||||
Rounding occurred though possibly no information was lost.
|
Rounding occurred though possibly no information was lost.
|
||||||
|
|
||||||
|
@ -844,16 +809,11 @@ The following table summarizes the hierarchy of signals:
|
||||||
Overflow(Inexact, Rounded)
|
Overflow(Inexact, Rounded)
|
||||||
Underflow(Inexact, Rounded, Subnormal)
|
Underflow(Inexact, Rounded, Subnormal)
|
||||||
InvalidOperation
|
InvalidOperation
|
||||||
ConversionSyntax
|
|
||||||
DivisionImpossible
|
|
||||||
DivisionUndefined(InvalidOperation, exceptions.ZeroDivisionError)
|
|
||||||
InvalidContext
|
|
||||||
Rounded
|
Rounded
|
||||||
Subnormal
|
Subnormal
|
||||||
\end{verbatim}
|
\end{verbatim}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
\subsection{Working with threads \label{decimal-threads}}
|
\subsection{Working with threads \label{decimal-threads}}
|
||||||
|
|
||||||
|
|
|
@ -78,30 +78,30 @@ Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
DivisionByZero: x / 0
|
DivisionByZero: x / 0
|
||||||
>>> c = Context()
|
>>> c = Context()
|
||||||
>>> c.trap_enablers[DivisionUndefined] = 0
|
>>> c.trap_enablers[InvalidOperation] = 0
|
||||||
>>> print c.flags[DivisionUndefined]
|
>>> print c.flags[InvalidOperation]
|
||||||
0
|
0
|
||||||
>>> c.divide(Decimal(0), Decimal(0))
|
>>> c.divide(Decimal(0), Decimal(0))
|
||||||
Decimal("NaN")
|
Decimal("NaN")
|
||||||
>>> c.trap_enablers[DivisionUndefined] = 1
|
>>> c.trap_enablers[InvalidOperation] = 1
|
||||||
>>> print c.flags[DivisionUndefined]
|
>>> print c.flags[InvalidOperation]
|
||||||
1
|
1
|
||||||
>>> c.flags[DivisionUndefined] = 0
|
>>> c.flags[InvalidOperation] = 0
|
||||||
>>> print c.flags[DivisionUndefined]
|
>>> print c.flags[InvalidOperation]
|
||||||
0
|
0
|
||||||
>>> print c.divide(Decimal(0), Decimal(0))
|
>>> print c.divide(Decimal(0), Decimal(0))
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
...
|
...
|
||||||
...
|
...
|
||||||
DivisionUndefined: 0 / 0
|
InvalidOperation: 0 / 0
|
||||||
>>> print c.flags[DivisionUndefined]
|
>>> print c.flags[InvalidOperation]
|
||||||
1
|
1
|
||||||
>>> c.flags[DivisionUndefined] = 0
|
>>> c.flags[InvalidOperation] = 0
|
||||||
>>> c.trap_enablers[DivisionUndefined] = False
|
>>> c.trap_enablers[InvalidOperation] = 0
|
||||||
>>> print c.divide(Decimal(0), Decimal(0))
|
>>> print c.divide(Decimal(0), Decimal(0))
|
||||||
NaN
|
NaN
|
||||||
>>> print c.flags[DivisionUndefined]
|
>>> print c.flags[InvalidOperation]
|
||||||
1
|
1
|
||||||
>>>
|
>>>
|
||||||
"""
|
"""
|
||||||
|
@ -152,7 +152,7 @@ ALWAYS_ROUND = 'ALWAYS_ROUND' # Every operation rounds at end.
|
||||||
#Errors
|
#Errors
|
||||||
|
|
||||||
class DecimalException(ArithmeticError):
|
class DecimalException(ArithmeticError):
|
||||||
"""Base exception class, defines default things.
|
"""Base exception class.
|
||||||
|
|
||||||
Used exceptions derive from this.
|
Used exceptions derive from this.
|
||||||
If an exception derives from another exception besides this (such as
|
If an exception derives from another exception besides this (such as
|
||||||
|
@ -160,12 +160,6 @@ 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.
|
||||||
|
|
||||||
Attributes:
|
|
||||||
|
|
||||||
default -- If the context is basic, the trap_enablers are set to
|
|
||||||
this by default. Extended contexts start out with them set
|
|
||||||
to 0, regardless.
|
|
||||||
|
|
||||||
handle -- Called when context._raise_error is called and the
|
handle -- Called when context._raise_error is called and the
|
||||||
trap_enabler is set. First argument is self, second is the
|
trap_enabler is set. First argument is self, second is the
|
||||||
context. More arguments can be given, those being after
|
context. More arguments can be given, those being after
|
||||||
|
@ -176,7 +170,6 @@ class DecimalException(ArithmeticError):
|
||||||
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.
|
||||||
"""
|
"""
|
||||||
default = 1
|
|
||||||
def handle(self, context, *args):
|
def handle(self, context, *args):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -288,7 +281,7 @@ class Inexact(DecimalException):
|
||||||
The inexact signal may be tested (or trapped) to determine if a given
|
The inexact signal may be tested (or trapped) to determine if a given
|
||||||
operation (or sequence of operations) was inexact.
|
operation (or sequence of operations) was inexact.
|
||||||
"""
|
"""
|
||||||
default = 0
|
pass
|
||||||
|
|
||||||
class InvalidContext(InvalidOperation):
|
class InvalidContext(InvalidOperation):
|
||||||
"""Invalid context. Unknown rounding, for example.
|
"""Invalid context. Unknown rounding, for example.
|
||||||
|
@ -315,7 +308,7 @@ class Rounded(DecimalException):
|
||||||
The rounded signal may be tested (or trapped) to determine if a given
|
The rounded signal may be tested (or trapped) to determine if a given
|
||||||
operation (or sequence of operations) caused a loss of precision.
|
operation (or sequence of operations) caused a loss of precision.
|
||||||
"""
|
"""
|
||||||
default = 0
|
pass
|
||||||
|
|
||||||
class Subnormal(DecimalException):
|
class Subnormal(DecimalException):
|
||||||
"""Exponent < Emin before rounding.
|
"""Exponent < Emin before rounding.
|
||||||
|
@ -382,19 +375,15 @@ 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.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# List of public traps and flags
|
||||||
|
Signals = [Clamped, DivisionByZero, Inexact, Overflow, Rounded,
|
||||||
|
Underflow, InvalidOperation, Subnormal]
|
||||||
|
|
||||||
def _filterfunc(obj):
|
# Map conditions (per the spec) to signals
|
||||||
"""Returns true if a subclass of DecimalException"""
|
_condition_map = {ConversionSyntax:InvalidOperation,
|
||||||
try:
|
DivisionImpossible:InvalidOperation,
|
||||||
return issubclass(obj, DecimalException)
|
DivisionUndefined:InvalidOperation,
|
||||||
except TypeError:
|
InvalidContext:InvalidOperation}
|
||||||
return False
|
|
||||||
|
|
||||||
#Signals holds the exceptions
|
|
||||||
Signals = filter(_filterfunc, globals().values())
|
|
||||||
|
|
||||||
del _filterfunc
|
|
||||||
|
|
||||||
|
|
||||||
##### Context Functions #######################################
|
##### Context Functions #######################################
|
||||||
|
|
||||||
|
@ -2168,7 +2157,7 @@ class Context(object):
|
||||||
return nc
|
return nc
|
||||||
__copy__ = copy
|
__copy__ = copy
|
||||||
|
|
||||||
def _raise_error(self, error, explanation = None, *args):
|
def _raise_error(self, condition, explanation = None, *args):
|
||||||
"""Handles an error
|
"""Handles an error
|
||||||
|
|
||||||
If the flag is in _ignored_flags, returns the default response.
|
If the flag is in _ignored_flags, returns the default response.
|
||||||
|
@ -2176,6 +2165,7 @@ class Context(object):
|
||||||
trap_enabler is set, it reaises the exception. Otherwise, it returns
|
trap_enabler is set, it reaises the exception. Otherwise, it returns
|
||||||
the default value after incrementing the flag.
|
the default value after incrementing the flag.
|
||||||
"""
|
"""
|
||||||
|
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)
|
||||||
|
@ -2183,7 +2173,7 @@ class Context(object):
|
||||||
self.flags[error] += 1
|
self.flags[error] += 1
|
||||||
if not self.trap_enablers[error]:
|
if not self.trap_enablers[error]:
|
||||||
#The errors define how to handle themselves.
|
#The errors define how to handle themselves.
|
||||||
return error().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 = []
|
||||||
|
@ -2207,6 +2197,11 @@ class Context(object):
|
||||||
for flag in flags:
|
for flag in flags:
|
||||||
self._ignored_flags.remove(flag)
|
self._ignored_flags.remove(flag)
|
||||||
|
|
||||||
|
def __hash__(self):
|
||||||
|
"""A Context cannot be hashed."""
|
||||||
|
# We inherit object.__hash__, so we must deny this explicitly
|
||||||
|
raise TypeError, "Cannot hash a Context."
|
||||||
|
|
||||||
def Etiny(self):
|
def Etiny(self):
|
||||||
"""Returns Etiny (= Emin - prec + 1)"""
|
"""Returns Etiny (= Emin - prec + 1)"""
|
||||||
return int(self.Emin - self.prec + 1)
|
return int(self.Emin - self.prec + 1)
|
||||||
|
|
|
@ -60,12 +60,12 @@ EXTENDEDERRORTEST = False
|
||||||
#Map the test cases' error names to the actual errors
|
#Map the test cases' error names to the actual errors
|
||||||
|
|
||||||
ErrorNames = {'clamped' : Clamped,
|
ErrorNames = {'clamped' : Clamped,
|
||||||
'conversion_syntax' : ConversionSyntax,
|
'conversion_syntax' : InvalidOperation,
|
||||||
'division_by_zero' : DivisionByZero,
|
'division_by_zero' : DivisionByZero,
|
||||||
'division_impossible' : DivisionImpossible,
|
'division_impossible' : InvalidOperation,
|
||||||
'division_undefined' : DivisionUndefined,
|
'division_undefined' : InvalidOperation,
|
||||||
'inexact' : Inexact,
|
'inexact' : Inexact,
|
||||||
'invalid_context' : InvalidContext,
|
'invalid_context' : InvalidOperation,
|
||||||
'invalid_operation' : InvalidOperation,
|
'invalid_operation' : InvalidOperation,
|
||||||
'overflow' : Overflow,
|
'overflow' : Overflow,
|
||||||
'rounded' : Rounded,
|
'rounded' : Rounded,
|
||||||
|
@ -131,6 +131,7 @@ class DecimalTest(unittest.TestCase):
|
||||||
return
|
return
|
||||||
for line in open(file).xreadlines():
|
for line in open(file).xreadlines():
|
||||||
line = line.replace('\r\n', '').replace('\n', '')
|
line = line.replace('\r\n', '').replace('\n', '')
|
||||||
|
#print line
|
||||||
try:
|
try:
|
||||||
t = self.eval_line(line)
|
t = self.eval_line(line)
|
||||||
except ConversionSyntax:
|
except ConversionSyntax:
|
||||||
|
@ -648,7 +649,6 @@ class DecimalArithmeticOperatorsTest(unittest.TestCase):
|
||||||
self.assertEqual(d1, Decimal('-0.625'))
|
self.assertEqual(d1, Decimal('-0.625'))
|
||||||
|
|
||||||
def test_floor_division(self):
|
def test_floor_division(self):
|
||||||
'''Test floor division in all its ways.'''
|
|
||||||
|
|
||||||
d1 = Decimal('5')
|
d1 = Decimal('5')
|
||||||
d2 = Decimal('2')
|
d2 = Decimal('2')
|
||||||
|
@ -676,7 +676,6 @@ class DecimalArithmeticOperatorsTest(unittest.TestCase):
|
||||||
self.assertEqual(d1, Decimal('1'))
|
self.assertEqual(d1, Decimal('1'))
|
||||||
|
|
||||||
def test_powering(self):
|
def test_powering(self):
|
||||||
'''Test powering in all its ways.'''
|
|
||||||
|
|
||||||
d1 = Decimal('5')
|
d1 = Decimal('5')
|
||||||
d2 = Decimal('2')
|
d2 = Decimal('2')
|
||||||
|
|
|
@ -26,6 +26,9 @@ Extension modules
|
||||||
Library
|
Library
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
- decimal.py now only uses signals in the spec. The other conditions are
|
||||||
|
no longer part of the public API.
|
||||||
|
|
||||||
Tools/Demos
|
Tools/Demos
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue