2004-01-29 14:59:56 -05:00
|
|
|
|
PEP: 327
|
|
|
|
|
Title: Decimal Data Type
|
|
|
|
|
Version: $Revision$
|
|
|
|
|
Last-Modified: $Date$
|
|
|
|
|
Author: Facundo Batista <facundo@taniquetil.com.ar>
|
2004-07-05 21:15:33 -04:00
|
|
|
|
Status: Final
|
2004-01-29 14:59:56 -05:00
|
|
|
|
Type: Standards Track
|
|
|
|
|
Content-Type: text/x-rst
|
|
|
|
|
Created: 17-Oct-2003
|
|
|
|
|
Python-Version: 2.4
|
2004-03-31 11:24:00 -05:00
|
|
|
|
Post-History: 30-Nov-2003, 02-Jan-2004, 29-Jan-2004
|
2004-01-29 14:59:56 -05:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Abstract
|
|
|
|
|
========
|
|
|
|
|
|
|
|
|
|
The idea is to have a Decimal data type, for every use where decimals
|
|
|
|
|
are needed but binary floating point is too inexact.
|
|
|
|
|
|
|
|
|
|
The Decimal data type will support the Python standard functions and
|
2004-06-16 10:27:00 -04:00
|
|
|
|
operations, and must comply with the decimal arithmetic ANSI standard
|
2004-01-29 14:59:56 -05:00
|
|
|
|
X3.274-1996 [1]_.
|
|
|
|
|
|
|
|
|
|
Decimal will be floating point (as opposed to fixed point) and will
|
|
|
|
|
have bounded precision (the precision is the upper limit on the
|
2004-06-16 10:27:00 -04:00
|
|
|
|
number of significant digits in a result). However, precision is
|
|
|
|
|
user-settable, and a notion of significant trailing zeroes is supported
|
|
|
|
|
so that fixed-point usage is also possible.
|
2004-01-29 14:59:56 -05:00
|
|
|
|
|
|
|
|
|
This work is based on code and test functions written by Eric Price,
|
|
|
|
|
Aahz and Tim Peters. Actually I'll work on the Decimal.py code in the
|
|
|
|
|
sandbox (at python/nondist/sandbox/decimal in the SourceForge CVS
|
|
|
|
|
repository). Much of the explanation in this PEP is taken from
|
2004-06-16 10:27:00 -04:00
|
|
|
|
Cowlishaw's work [2]_, comp.lang.python and python-dev.
|
2004-01-29 14:59:56 -05:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Motivation
|
|
|
|
|
==========
|
|
|
|
|
|
|
|
|
|
Here I'll expose the reasons of why I think a Decimal data type is
|
2004-06-16 10:27:00 -04:00
|
|
|
|
needed and why other numeric data types are not enough.
|
2004-01-29 14:59:56 -05:00
|
|
|
|
|
|
|
|
|
I wanted a Money data type, and after proposing a pre-PEP in
|
|
|
|
|
comp.lang.python, the community agreed to have a numeric data type
|
|
|
|
|
with the needed arithmetic behaviour, and then build Money over it:
|
|
|
|
|
all the considerations about quantity of digits after the decimal
|
|
|
|
|
point, rounding, etc., will be handled through Money. It is not the
|
|
|
|
|
purpose of this PEP to have a data type that can be used as Money
|
|
|
|
|
without further effort.
|
|
|
|
|
|
|
|
|
|
One of the biggest advantages of implementing a standard is that
|
2004-06-16 10:27:00 -04:00
|
|
|
|
someone already thought out all the creepy cases for you. And to a
|
2004-01-29 14:59:56 -05:00
|
|
|
|
standard GvR redirected me: Mike Cowlishaw's General Decimal
|
|
|
|
|
Arithmetic specification [2]_. This document defines a general
|
|
|
|
|
purpose decimal arithmetic. A correct implementation of this
|
|
|
|
|
specification will conform to the decimal arithmetic defined in
|
|
|
|
|
ANSI/IEEE standard 854-1987, except for some minor restrictions, and
|
|
|
|
|
will also provide unrounded decimal arithmetic and integer arithmetic
|
|
|
|
|
as proper subsets.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The problem with binary float
|
|
|
|
|
-----------------------------
|
|
|
|
|
|
|
|
|
|
In decimal math, there are many numbers that can't be represented with
|
|
|
|
|
a fixed number of decimal digits, e.g. 1/3 = 0.3333333333.......
|
|
|
|
|
|
|
|
|
|
In base 2 (the way that standard floating point is calculated), 1/2 =
|
|
|
|
|
0.1, 1/4 = 0.01, 1/8 = 0.001, etc. Decimal 0.2 equals 2/10 equals
|
|
|
|
|
1/5, resulting in the binary fractional number
|
|
|
|
|
0.001100110011001... As you can see, the problem is that some decimal
|
|
|
|
|
numbers can't be represented exactly in binary, resulting in small
|
|
|
|
|
roundoff errors.
|
|
|
|
|
|
|
|
|
|
So we need a decimal data type that represents exactly decimal
|
|
|
|
|
numbers. Instead of a binary data type, we need a decimal one.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Why floating point?
|
|
|
|
|
-------------------
|
|
|
|
|
|
|
|
|
|
So we go to decimal, but why *floating point*?
|
|
|
|
|
|
|
|
|
|
Floating point numbers use a fixed quantity of digits (precision) to
|
|
|
|
|
represent a number, working with an exponent when the number gets too
|
|
|
|
|
big or too small. For example, with a precision of 5::
|
|
|
|
|
|
|
|
|
|
1234 ==> 1234e0
|
|
|
|
|
12345 ==> 12345e0
|
2004-06-16 10:27:00 -04:00
|
|
|
|
123456 ==> 12346e1
|
|
|
|
|
|
|
|
|
|
(note that in the last line the number got rounded to fit in five digits).
|
2004-01-29 14:59:56 -05:00
|
|
|
|
|
|
|
|
|
In contrast, we have the example of a ``long`` integer with infinite
|
|
|
|
|
precision, meaning that you can have the number as big as you want,
|
|
|
|
|
and you'll never lose any information.
|
|
|
|
|
|
|
|
|
|
In a fixed point number, the position of the decimal point is fixed.
|
|
|
|
|
For a fixed point data type, check Tim Peter's FixedPoint at
|
|
|
|
|
SourceForge [4]_. I'll go for floating point because it's easier to
|
|
|
|
|
implement the arithmetic behaviour of the standard, and then you can
|
|
|
|
|
implement a fixed point data type over Decimal.
|
|
|
|
|
|
|
|
|
|
But why can't we have a floating point number with infinite precision?
|
|
|
|
|
It's not so easy, because of inexact divisions. E.g.: 1/3 =
|
|
|
|
|
0.3333333333333... ad infinitum. In this case you should store a
|
|
|
|
|
infinite amount of 3s, which takes too much memory, ;).
|
|
|
|
|
|
|
|
|
|
John Roth proposed to eliminate the division operator and force the
|
|
|
|
|
user to use an explicit method, just to avoid this kind of trouble.
|
|
|
|
|
This generated adverse reactions in comp.lang.python, as everybody
|
|
|
|
|
wants to have support for the ``/`` operator in a numeric data type.
|
|
|
|
|
|
|
|
|
|
With this exposed maybe you're thinking "Hey! Can we just store the 1
|
2004-06-16 10:27:00 -04:00
|
|
|
|
and the 3 as numerator and denominator?", which takes us to the next
|
2004-01-29 14:59:56 -05:00
|
|
|
|
point.
|
|
|
|
|
|
|
|
|
|
|
2004-06-16 10:27:00 -04:00
|
|
|
|
Why not rational?
|
|
|
|
|
-----------------
|
2004-01-29 14:59:56 -05:00
|
|
|
|
|
|
|
|
|
Rational numbers are stored using two integer numbers, the numerator
|
|
|
|
|
and the denominator. This implies that the arithmetic operations
|
|
|
|
|
can't be executed directly (e.g. to add two rational numbers you first
|
|
|
|
|
need to calculate the common denominator).
|
|
|
|
|
|
|
|
|
|
Quoting Alex Martelli:
|
|
|
|
|
|
|
|
|
|
The performance implications of the fact that summing two
|
|
|
|
|
rationals (which take O(M) and O(N) space respectively) gives a
|
|
|
|
|
rational which takes O(M+N) memory space is just too troublesome.
|
|
|
|
|
There are excellent Rational implementations in both pure Python
|
|
|
|
|
and as extensions (e.g., gmpy), but they'll always be a "niche
|
|
|
|
|
market" IMHO. Probably worth PEPping, not worth doing without
|
|
|
|
|
Decimal -- which is the right way to represent sums of money, a
|
|
|
|
|
truly major use case in the real world.
|
|
|
|
|
|
|
|
|
|
Anyway, if you're interested in this data type, you maybe will want to
|
|
|
|
|
take a look at PEP 239: Adding a Rational Type to Python.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
So, what do we have?
|
|
|
|
|
--------------------
|
|
|
|
|
|
|
|
|
|
The result is a Decimal data type, with bounded precision and floating
|
|
|
|
|
point.
|
|
|
|
|
|
|
|
|
|
Will it be useful? I can't say it better than Alex Martelli:
|
|
|
|
|
|
|
|
|
|
Python (out of the box) doesn't let you have binary floating point
|
|
|
|
|
numbers *with whatever precision you specify*: you're limited to
|
|
|
|
|
what your hardware supplies. Decimal, be it used as a fixed or
|
|
|
|
|
floating point number, should suffer from no such limitation:
|
|
|
|
|
whatever bounded precision you may specify on number creation
|
|
|
|
|
(your memory permitting) should work just as well. Most of the
|
|
|
|
|
expense of programming simplicity can be hidden from application
|
|
|
|
|
programs and placed in a suitable decimal arithmetic type. As per
|
|
|
|
|
http://www2.hursley.ibm.com/decimal/, *a single data type can be
|
|
|
|
|
used for integer, fixed-point, and floating-point decimal
|
|
|
|
|
arithmetic* -- and for money arithmetic which doesn't drive the
|
|
|
|
|
application programmer crazy.
|
|
|
|
|
|
|
|
|
|
There are several uses for such a data type. As I said before, I will
|
|
|
|
|
use it as base for Money. In this case the bounded precision is not
|
|
|
|
|
an issue; quoting Tim Peters:
|
|
|
|
|
|
|
|
|
|
A precision of 20 would be way more than enough to account for
|
|
|
|
|
total world economic output, down to the penny, since the
|
|
|
|
|
beginning of time.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
General Decimal Arithmetic Specification
|
|
|
|
|
========================================
|
|
|
|
|
|
|
|
|
|
Here I'll include information and descriptions that are part of the
|
|
|
|
|
specification [2]_ (the structure of the number, the context, etc.).
|
|
|
|
|
All the requirements included in this section are not for discussion
|
|
|
|
|
(barring typos or other mistakes), as they are in the standard, and
|
|
|
|
|
the PEP is just for implementing the standard.
|
|
|
|
|
|
|
|
|
|
Because of copyright restrictions, I can not copy here explanations
|
|
|
|
|
taken from the specification, so I'll try to explain it in my own
|
|
|
|
|
words. I firmly encourage you to read the original specification
|
|
|
|
|
document [2]_ for details or if you have any doubt.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The Arithmetic Model
|
|
|
|
|
--------------------
|
|
|
|
|
|
|
|
|
|
The specification is based on a decimal arithmetic model, as defined
|
|
|
|
|
by the relevant standards: IEEE 854 [3]_, ANSI X3-274 [1]_, and the
|
|
|
|
|
proposed revision [5]_ of IEEE 754 [6]_.
|
|
|
|
|
|
|
|
|
|
The model has three components:
|
|
|
|
|
|
|
|
|
|
- Numbers: just the values that the operation uses as input or output.
|
|
|
|
|
|
|
|
|
|
- Operations: addition, multiplication, etc.
|
|
|
|
|
|
|
|
|
|
- Context: a set of parameters and rules that the user can select and
|
|
|
|
|
which govern the results of operations (for example, the precision
|
|
|
|
|
to be used).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Numbers
|
|
|
|
|
-------
|
|
|
|
|
|
|
|
|
|
Numbers may be finite or special values. The former can be
|
|
|
|
|
represented exactly. The latter are infinites and undefined (such as
|
|
|
|
|
0/0).
|
|
|
|
|
|
|
|
|
|
Finite numbers are defined by three parameters:
|
|
|
|
|
|
|
|
|
|
- Sign: 0 (positive) or 1 (negative).
|
|
|
|
|
|
|
|
|
|
- Coefficient: a non-negative integer.
|
|
|
|
|
|
|
|
|
|
- Exponent: a signed integer, the power of ten of the coefficient
|
|
|
|
|
multiplier.
|
|
|
|
|
|
|
|
|
|
The numerical value of a finite number is given by::
|
|
|
|
|
|
|
|
|
|
(-1)**sign * coefficient * 10**exponent
|
|
|
|
|
|
|
|
|
|
Special values are named as following:
|
|
|
|
|
|
|
|
|
|
- Infinity: a value which is infinitely large. Could be positive or
|
|
|
|
|
negative.
|
|
|
|
|
|
|
|
|
|
- Quiet NaN ("qNaN"): represent undefined results (*Not a Number*).
|
|
|
|
|
Does not cause an Invalid operation condition. The sign in a NaN
|
|
|
|
|
has no meaning.
|
|
|
|
|
|
|
|
|
|
- Signaling NaN ("sNaN"): also *Not a Number*, but will cause an
|
|
|
|
|
Invalid operation condition if used in any operation.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Context
|
|
|
|
|
-------
|
|
|
|
|
|
|
|
|
|
The context is a set of parameters and rules that the user can select
|
|
|
|
|
and which govern the results of operations (for example, the precision
|
|
|
|
|
to be used).
|
|
|
|
|
|
2004-06-16 10:27:00 -04:00
|
|
|
|
The context gets that name because it surrounds the Decimal numbers,
|
|
|
|
|
with parts of context acting as input to, and output of, operations.
|
|
|
|
|
It's up to the application to work with one or several contexts,
|
2004-01-29 14:59:56 -05:00
|
|
|
|
but definitely the idea is not to get a context per Decimal number.
|
2004-06-16 10:27:00 -04:00
|
|
|
|
For example, a typical use would be to set the context's precision to
|
|
|
|
|
20 digits at the start of a program, and never explicitly use context
|
|
|
|
|
again.
|
2004-01-29 14:59:56 -05:00
|
|
|
|
|
|
|
|
|
These definitions don't affect the internal storage of the Decimal
|
|
|
|
|
numbers, just the way that the arithmetic operations are performed.
|
|
|
|
|
|
2004-06-16 10:27:00 -04:00
|
|
|
|
The context is mainly defined by the following parameters (see
|
|
|
|
|
`Context Attributes`_ for all context attributes):
|
2004-01-29 14:59:56 -05:00
|
|
|
|
|
|
|
|
|
- Precision: The maximum number of significant digits that can result
|
2004-06-16 10:27:00 -04:00
|
|
|
|
from an arithmetic operation (integer > 0). There is no maximum for
|
|
|
|
|
this value.
|
2004-01-29 14:59:56 -05:00
|
|
|
|
|
|
|
|
|
- Rounding: The name of the algorithm to be used when rounding is
|
|
|
|
|
necessary, one of "round-down", "round-half-up", "round-half-even",
|
|
|
|
|
"round-ceiling", "round-floor", "round-half-down", and "round-up".
|
|
|
|
|
See `Rounding Algorithms`_ below.
|
|
|
|
|
|
|
|
|
|
- Flags and trap-enablers: `Exceptional conditions`_ are grouped into
|
|
|
|
|
signals, controllable individually, each consisting of a flag
|
|
|
|
|
(boolean, set when the signal occurs) and a trap-enabler (a boolean
|
|
|
|
|
that controls behavior). The signals are: "clamped",
|
|
|
|
|
"division-by-zero", "inexact", "invalid-operation", "overflow",
|
|
|
|
|
"rounded", "subnormal" and "underflow".
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Default Contexts
|
|
|
|
|
----------------
|
|
|
|
|
|
|
|
|
|
The specification defines two default contexts, which should be easily
|
|
|
|
|
selectable by the user.
|
|
|
|
|
|
|
|
|
|
Basic Default Context:
|
|
|
|
|
|
|
|
|
|
- flags: all set to 0
|
|
|
|
|
- trap-enablers: inexact, rounded, and subnormal are set to 0; all
|
|
|
|
|
others are set to 1
|
|
|
|
|
- precision: is set to 9
|
|
|
|
|
- rounding: is set to round-half-up
|
|
|
|
|
|
|
|
|
|
Extended Default Context:
|
|
|
|
|
|
|
|
|
|
- flags: all set to 0
|
|
|
|
|
- trap-enablers: all set to 0
|
2004-06-16 10:27:00 -04:00
|
|
|
|
- precision: is set to 9
|
2004-01-29 14:59:56 -05:00
|
|
|
|
- rounding: is set to round-half-even
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Exceptional Conditions
|
|
|
|
|
----------------------
|
|
|
|
|
|
|
|
|
|
The table below lists the exceptional conditions that may arise during
|
|
|
|
|
the arithmetic operations, the corresponding signal, and the defined
|
|
|
|
|
result. For details, see the specification [2]_.
|
|
|
|
|
|
|
|
|
|
==================== ================= ===================================
|
|
|
|
|
Condition Signal Result
|
|
|
|
|
==================== ================= ===================================
|
|
|
|
|
Clamped clamped see spec [2]_
|
|
|
|
|
Conversion syntax invalid-operation [0,qNaN]
|
|
|
|
|
Division by zero division-by-zero [sign,inf]
|
|
|
|
|
Division impossible invalid-operation [0,qNaN]
|
|
|
|
|
Division undefined invalid-operation [0,qNaN]
|
|
|
|
|
Inexact inexact unchanged
|
|
|
|
|
Invalid context invalid-operation [0,qNaN]
|
|
|
|
|
Invalid operation invalid-operation [0,qNaN] (or [s,qNaN] or [s,qNaN,d]
|
|
|
|
|
when the cause is a signaling NaN)
|
|
|
|
|
Overflow overflow depends on the rounding mode
|
|
|
|
|
Rounded rounded unchanged
|
|
|
|
|
Subnormal subnormal unchanged
|
|
|
|
|
Underflow underflow see spec [2]_
|
|
|
|
|
==================== ================= ===================================
|
|
|
|
|
|
2004-06-16 10:27:00 -04:00
|
|
|
|
Note: when the standard talks about "Insufficient storage", as long as
|
|
|
|
|
this is implementation-specific behaviour about not having enough
|
|
|
|
|
storage to keep the internals of the number, this implementation will
|
|
|
|
|
raise MemoryError.
|
|
|
|
|
|
|
|
|
|
Regarding Overflow and Underflow, there's been a long discussion in
|
|
|
|
|
python-dev about artificial limits. The general consensus is to keep
|
|
|
|
|
the artificial limits only if there are important reasons to do that.
|
|
|
|
|
Tim Peters gives us three:
|
|
|
|
|
|
|
|
|
|
...eliminating bounds on exponents effectively means overflow
|
|
|
|
|
(and underflow) can never happen. But overflow *is* a valuable
|
|
|
|
|
safety net in real life fp use, like a canary in a coal mine,
|
|
|
|
|
giving danger signs early when a program goes insane.
|
|
|
|
|
|
|
|
|
|
Virtually all implementations of 854 use (and as IBM's standard
|
|
|
|
|
even suggests) "forbidden" exponent values to encode non-finite
|
|
|
|
|
numbers (infinities and NaNs). A bounded exponent can do this at
|
|
|
|
|
virtually no extra storage cost. If the exponent is unbounded,
|
|
|
|
|
then additional bits have to be used instead. This cost remains
|
|
|
|
|
hidden until more time- and space- efficient implementations are
|
|
|
|
|
attempted.
|
|
|
|
|
|
|
|
|
|
Big as it is, the IBM standard is a tiny start at supplying a
|
|
|
|
|
complete numeric facility. Having no bound on exponent size will
|
|
|
|
|
enormously complicate the implementations of, e.g., decimal sin()
|
|
|
|
|
and cos() (there's then no a priori limit on how many digits of
|
|
|
|
|
pi effectively need to be known in order to perform argument
|
|
|
|
|
reduction).
|
|
|
|
|
|
|
|
|
|
Edward Loper give us an example of when the limits are to be crossed:
|
|
|
|
|
probabilities.
|
|
|
|
|
|
|
|
|
|
That said, Robert Brewer and Andrew Lentvorski want the limits to be
|
|
|
|
|
easily modifiable by the users. Actually, this is quite posible::
|
|
|
|
|
|
|
|
|
|
>>> d1 = Decimal("1e999999999") # at the exponent limit
|
|
|
|
|
>>> d1
|
|
|
|
|
Decimal( (0, (1,), 999999999L) )
|
|
|
|
|
>>> d1 * 10 # exceed the limit, got infinity
|
|
|
|
|
Decimal( (0, (0,), 'F') )
|
|
|
|
|
>>> getcontext().Emax = 1000000000 # increase the limit
|
|
|
|
|
>>> d1 * 10 # does not exceed any more
|
|
|
|
|
Decimal( (0, (1, 0), 999999999L) )
|
|
|
|
|
>>> d1 * 100 # exceed again
|
|
|
|
|
Decimal( (0, (0,), 'F') )
|
|
|
|
|
|
2004-01-29 14:59:56 -05:00
|
|
|
|
|
|
|
|
|
Rounding Algorithms
|
|
|
|
|
-------------------
|
|
|
|
|
|
|
|
|
|
``round-down``: The discarded digits are ignored; the result is
|
|
|
|
|
unchanged (round toward 0, truncate)::
|
|
|
|
|
|
|
|
|
|
1.123 --> 1.12
|
|
|
|
|
1.128 --> 1.12
|
|
|
|
|
1.125 --> 1.12
|
|
|
|
|
1.135 --> 1.13
|
|
|
|
|
|
|
|
|
|
``round-half-up``: If the discarded digits represent greater than or
|
|
|
|
|
equal to half (0.5) then the result should be incremented by 1;
|
|
|
|
|
otherwise the discarded digits are ignored::
|
|
|
|
|
|
|
|
|
|
1.123 --> 1.12
|
|
|
|
|
1.128 --> 1.13
|
|
|
|
|
1.125 --> 1.13
|
|
|
|
|
1.135 --> 1.14
|
|
|
|
|
|
|
|
|
|
``round-half-even``: If the discarded digits represent greater than
|
|
|
|
|
half (0.5) then the result coefficient is incremented by 1; if they
|
|
|
|
|
represent less than half, then the result is not adjusted; otherwise
|
|
|
|
|
the result is unaltered if its rightmost digit is even, or incremented
|
|
|
|
|
by 1 if its rightmost digit is odd (to make an even digit)::
|
|
|
|
|
|
|
|
|
|
1.123 --> 1.12
|
|
|
|
|
1.128 --> 1.13
|
|
|
|
|
1.125 --> 1.12
|
|
|
|
|
1.135 --> 1.14
|
|
|
|
|
|
|
|
|
|
``round-ceiling``: If all of the discarded digits are zero or if the
|
|
|
|
|
sign is negative the result is unchanged; otherwise, the result is
|
2004-06-16 10:27:00 -04:00
|
|
|
|
incremented by 1 (round toward positive infinity)::
|
2004-01-29 14:59:56 -05:00
|
|
|
|
|
|
|
|
|
1.123 --> 1.13
|
|
|
|
|
1.128 --> 1.13
|
|
|
|
|
-1.123 --> -1.12
|
|
|
|
|
-1.128 --> -1.12
|
|
|
|
|
|
|
|
|
|
``round-floor``: If all of the discarded digits are zero or if the
|
|
|
|
|
sign is positive the result is unchanged; otherwise, the absolute
|
2004-06-16 10:27:00 -04:00
|
|
|
|
value of the result is incremented by 1 (round toward negative
|
|
|
|
|
infinty)::
|
2004-01-29 14:59:56 -05:00
|
|
|
|
|
|
|
|
|
1.123 --> 1.12
|
|
|
|
|
1.128 --> 1.12
|
|
|
|
|
-1.123 --> -1.13
|
|
|
|
|
-1.128 --> -1.13
|
|
|
|
|
|
|
|
|
|
``round-half-down``: If the discarded digits represent greater than
|
|
|
|
|
half (0.5) then the result is incremented by 1; otherwise the
|
|
|
|
|
discarded digits are ignored::
|
|
|
|
|
|
|
|
|
|
1.123 --> 1.12
|
|
|
|
|
1.128 --> 1.13
|
|
|
|
|
1.125 --> 1.12
|
|
|
|
|
1.135 --> 1.13
|
|
|
|
|
|
|
|
|
|
``round-up``: If all of the discarded digits are zero the result is
|
|
|
|
|
unchanged, otherwise the result is incremented by 1 (round away from
|
|
|
|
|
0)::
|
|
|
|
|
|
|
|
|
|
1.123 --> 1.13
|
|
|
|
|
1.128 --> 1.13
|
|
|
|
|
1.125 --> 1.13
|
|
|
|
|
1.135 --> 1.14
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Rationale
|
|
|
|
|
=========
|
|
|
|
|
|
|
|
|
|
I must separate the requirements in two sections. The first is to
|
|
|
|
|
comply with the ANSI standard. All the requirements for this are
|
|
|
|
|
specified in the Mike Cowlishaw's work [2]_. He also provided a
|
2004-06-16 10:27:00 -04:00
|
|
|
|
**very large** suite of test cases.
|
2004-01-29 14:59:56 -05:00
|
|
|
|
|
|
|
|
|
The second section of requirements (standard Python functions support,
|
|
|
|
|
usability, etc.) is detailed from here, where I'll include all the
|
|
|
|
|
decisions made and why, and all the subjects still being discussed.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Explicit construction
|
|
|
|
|
---------------------
|
|
|
|
|
|
|
|
|
|
The explicit construction does not get affected by the context (there
|
|
|
|
|
is no rounding, no limits by the precision, etc.), because the context
|
2004-06-16 10:27:00 -04:00
|
|
|
|
affects just operations' results. The only exception to this is when
|
|
|
|
|
you're `Creating from Context`_.
|
2004-01-29 14:59:56 -05:00
|
|
|
|
|
2004-03-31 11:24:00 -05:00
|
|
|
|
|
|
|
|
|
From int or long
|
|
|
|
|
''''''''''''''''
|
|
|
|
|
|
|
|
|
|
There's no loss and no need to specify any other information::
|
2004-01-29 14:59:56 -05:00
|
|
|
|
|
|
|
|
|
Decimal(35)
|
|
|
|
|
Decimal(-124)
|
|
|
|
|
|
2004-03-31 11:24:00 -05:00
|
|
|
|
|
|
|
|
|
From string
|
|
|
|
|
'''''''''''
|
|
|
|
|
|
2004-06-16 10:27:00 -04:00
|
|
|
|
Strings containing Python decimal integer literals and Python float
|
|
|
|
|
literals will be supported. In this transformation there is no loss
|
|
|
|
|
of information, as the string is directly converted to Decimal (there
|
|
|
|
|
is not an intermediate conversion through float)::
|
2004-01-29 14:59:56 -05:00
|
|
|
|
|
|
|
|
|
Decimal("-12")
|
|
|
|
|
Decimal("23.2e-7")
|
|
|
|
|
|
2004-06-16 10:27:00 -04:00
|
|
|
|
Also, you can construct in this way all special values (Infinity and
|
|
|
|
|
Not a Number)::
|
|
|
|
|
|
|
|
|
|
Decimal("Inf")
|
|
|
|
|
Decimal("NaN")
|
|
|
|
|
|
2004-03-31 11:24:00 -05:00
|
|
|
|
|
|
|
|
|
From float
|
|
|
|
|
''''''''''
|
|
|
|
|
|
|
|
|
|
The initial discussion on this item was what should
|
2004-01-29 14:59:56 -05:00
|
|
|
|
happen when passing floating point to the constructor:
|
|
|
|
|
|
|
|
|
|
1. ``Decimal(1.1) == Decimal('1.1')``
|
|
|
|
|
|
|
|
|
|
2. ``Decimal(1.1) ==
|
|
|
|
|
Decimal('110000000000000008881784197001252...e-51')``
|
|
|
|
|
|
|
|
|
|
3. an exception is raised
|
|
|
|
|
|
|
|
|
|
Several people alleged that (1) is the better option here, because
|
|
|
|
|
it's what you expect when writing ``Decimal(1.1)``. And quoting John
|
|
|
|
|
Roth, it's easy to implement:
|
|
|
|
|
|
|
|
|
|
It's not at all difficult to find where the actual number ends and
|
|
|
|
|
where the fuzz begins. You can do it visually, and the algorithms
|
|
|
|
|
to do it are quite well known.
|
|
|
|
|
|
|
|
|
|
But If I *really* want my number to be
|
2004-06-16 10:27:00 -04:00
|
|
|
|
``Decimal('110000000000000008881784197001252...e-51')``, why can't I
|
2004-01-29 14:59:56 -05:00
|
|
|
|
write ``Decimal(1.1)``? Why should I expect Decimal to be "rounding"
|
|
|
|
|
it? Remember that ``1.1`` *is* binary floating point, so I can
|
|
|
|
|
predict the result. It's not intuitive to a beginner, but that's the
|
|
|
|
|
way it is.
|
|
|
|
|
|
2004-06-16 10:27:00 -04:00
|
|
|
|
Anyway, Paul Moore showed that (1) can't work, because::
|
2004-01-29 14:59:56 -05:00
|
|
|
|
|
|
|
|
|
(1) says D(1.1) == D('1.1')
|
|
|
|
|
but 1.1 == 1.1000000000000001
|
|
|
|
|
so D(1.1) == D(1.1000000000000001)
|
|
|
|
|
together: D(1.1000000000000001) == D('1.1')
|
|
|
|
|
|
|
|
|
|
which is wrong, because if I write ``Decimal('1.1')`` it is exact, not
|
|
|
|
|
``D(1.1000000000000001)``. He also proposed to have an explicit
|
|
|
|
|
conversion to float. bokr says you need to put the precision in the
|
2004-06-16 10:27:00 -04:00
|
|
|
|
constructor and mwilson agreed::
|
2004-01-29 14:59:56 -05:00
|
|
|
|
|
|
|
|
|
d = Decimal (1.1, 1) # take float value to 1 decimal place
|
|
|
|
|
d = Decimal (1.1) # gets `places` from pre-set context
|
|
|
|
|
|
|
|
|
|
But Alex Martelli says that:
|
|
|
|
|
|
|
|
|
|
Constructing with some specified precision would be fine. Thus,
|
|
|
|
|
I think "construction from float with some default precision" runs
|
|
|
|
|
a substantial risk of tricking naive users.
|
|
|
|
|
|
2004-03-31 11:24:00 -05:00
|
|
|
|
So, the accepted solution through c.l.p is that you can not call Decimal
|
|
|
|
|
with a float. Instead you must use a method: Decimal.from_float(). The
|
|
|
|
|
syntax::
|
2004-01-29 14:59:56 -05:00
|
|
|
|
|
2004-06-16 10:27:00 -04:00
|
|
|
|
Decimal.from_float(floatNumber, [decimal_places])
|
|
|
|
|
|
|
|
|
|
where ``floatNumber`` is the float number origin of the construction
|
|
|
|
|
and ``decimal_places`` are the number of digits after the decimal
|
|
|
|
|
point where you apply a round-half-up rounding, if any. In this way
|
|
|
|
|
you can do, for example::
|
2004-01-29 14:59:56 -05:00
|
|
|
|
|
2004-06-16 10:27:00 -04:00
|
|
|
|
Decimal.from_float(1.1, 2): The same as doing Decimal('1.1').
|
|
|
|
|
Decimal.from_float(1.1, 16): The same as doing Decimal('1.1000000000000001').
|
|
|
|
|
Decimal.from_float(1.1): The same as doing Decimal('1100000000000000088817841970012523233890533447265625e-51').
|
2004-03-31 11:24:00 -05:00
|
|
|
|
|
2004-06-29 23:37:13 -04:00
|
|
|
|
Based on later discussions, it was decided to omit from_float() from the
|
|
|
|
|
API for Py2.4. Several ideas contributed to the thought process:
|
|
|
|
|
|
|
|
|
|
- Interactions between decimal and binary floating point force the user to
|
|
|
|
|
deal with tricky issues of representation and round-off. Avoidance of those
|
|
|
|
|
issues is a primary reason for having the module in the first place.
|
|
|
|
|
|
|
|
|
|
- The first release of the module should focus on that which is safe, minimal,
|
|
|
|
|
and essential.
|
|
|
|
|
|
|
|
|
|
- While theoretically nice, real world use cases for interactions between
|
|
|
|
|
and decimals are lacking. Java included float/decimal conversions to handle
|
|
|
|
|
an obscure case where calculations are best performed in decimal eventhough
|
|
|
|
|
a legacy data structure requires the inputs and outputs to be stored in
|
|
|
|
|
binary floating point.
|
|
|
|
|
|
|
|
|
|
- If the need arises, users can use string representations as an intermediate
|
|
|
|
|
type. The advantage of this approach is that it makes explicit the
|
|
|
|
|
assumptions about precision and representation (no wondering what is going
|
|
|
|
|
on under the hood).
|
|
|
|
|
|
|
|
|
|
- The Java docs for BigDecimal(double val) reflected their experiences with
|
|
|
|
|
the constructor::
|
|
|
|
|
|
|
|
|
|
The results of this constructor can be somewhat
|
|
|
|
|
unpredictable and its use is generally not recommended.
|
|
|
|
|
|
2004-03-31 11:24:00 -05:00
|
|
|
|
|
|
|
|
|
From tuples
|
|
|
|
|
'''''''''''
|
|
|
|
|
|
|
|
|
|
Aahz suggested to construct from tuples: it's easier
|
2004-01-29 14:59:56 -05:00
|
|
|
|
to implement ``eval()``'s round trip and "someone who has numeric
|
|
|
|
|
values representing a Decimal does not need to convert them to a
|
|
|
|
|
string."
|
|
|
|
|
|
|
|
|
|
The structure will be a tuple of three elements: sign, number and
|
|
|
|
|
exponent. The sign is 1 or 0, the number is a tuple of decimal digits
|
|
|
|
|
and the exponent is a signed int or long::
|
|
|
|
|
|
|
|
|
|
Decimal((1, (3, 2, 2, 5), -2)) # for -32.25
|
|
|
|
|
|
2004-06-16 10:27:00 -04:00
|
|
|
|
Of course, you can construct in this way all special values::
|
|
|
|
|
|
|
|
|
|
Decimal( (0, (0,), 'F') ) # for Infinity
|
|
|
|
|
Decimal( (0, (0,), 'n') ) # for Not a Number
|
|
|
|
|
|
2004-01-29 14:59:56 -05:00
|
|
|
|
|
2004-03-31 11:24:00 -05:00
|
|
|
|
From Decimal
|
|
|
|
|
''''''''''''
|
|
|
|
|
|
|
|
|
|
No mystery here, just a copy.
|
|
|
|
|
|
2004-01-29 14:59:56 -05:00
|
|
|
|
|
2004-03-31 11:24:00 -05:00
|
|
|
|
Syntax for All Cases
|
|
|
|
|
''''''''''''''''''''
|
2004-01-29 14:59:56 -05:00
|
|
|
|
|
2004-03-31 11:24:00 -05:00
|
|
|
|
::
|
|
|
|
|
|
|
|
|
|
Decimal(value1)
|
2004-06-16 10:27:00 -04:00
|
|
|
|
Decimal.from_float(value2, [decimal_places])
|
|
|
|
|
|
|
|
|
|
where ``value1`` can be int, long, string, 3-tuple or Decimal,
|
|
|
|
|
``value2`` can only be float, and ``decimal_places`` is an optional
|
|
|
|
|
non negative int.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Creating from Context
|
|
|
|
|
'''''''''''''''''''''
|
|
|
|
|
|
|
|
|
|
This item arose in python-dev from two sources in parallel. Ka-Ping
|
|
|
|
|
Yee proposes to pass the context as an argument at instance creation
|
|
|
|
|
(he wants the context he passes to be used only in creation time: "It
|
|
|
|
|
would not be persistent"). Tony Meyer asks from_string to honor the
|
|
|
|
|
context if it receives a parameter "honour_context" with a True value.
|
|
|
|
|
(I don't like it, because the doc specifies that the context be
|
|
|
|
|
honored and I don't want the method to comply with the specification
|
|
|
|
|
regarding the value of an argument.)
|
|
|
|
|
|
|
|
|
|
Tim Peters gives us a reason to have a creation that uses context:
|
|
|
|
|
|
|
|
|
|
In general number-crunching, literals may be given to high
|
|
|
|
|
precision, but that precision isn't free and *usually* isn't
|
|
|
|
|
needed
|
|
|
|
|
|
|
|
|
|
Casey Duncan wants to use another method, not a bool arg:
|
|
|
|
|
|
|
|
|
|
I find boolean arguments a general anti-pattern, especially given
|
|
|
|
|
we have class methods. Why not use an alternate constructor like
|
|
|
|
|
Decimal.rounded_to_context("3.14159265").
|
2004-03-31 11:24:00 -05:00
|
|
|
|
|
2004-06-16 10:27:00 -04:00
|
|
|
|
In the process of deciding the syntax of that, Tim came up with a
|
|
|
|
|
better idea: he proposes not to have a method in Decimal to create
|
|
|
|
|
with a different context, but having instead a method in Context to
|
|
|
|
|
create a Decimal instance. Basically, instead of::
|
|
|
|
|
|
|
|
|
|
D.using_context(number, context)
|
|
|
|
|
|
|
|
|
|
it will be::
|
|
|
|
|
|
|
|
|
|
context.create_decimal(number)
|
|
|
|
|
|
|
|
|
|
From Tim:
|
|
|
|
|
|
|
|
|
|
While all operations in the spec except for the two to-string
|
|
|
|
|
operations use context, no operations in the spec support an
|
|
|
|
|
optional local context. That the Decimal() constructor ignores
|
|
|
|
|
context by default is an extension to the spec. We must supply a
|
|
|
|
|
context-honoring from-string operation to meet the spec. I
|
|
|
|
|
recommend against any concept of "local context" in any operation
|
|
|
|
|
-- it complicates the model and isn't necessary.
|
|
|
|
|
|
|
|
|
|
So, we decided to use a context method to create a Decimal that will
|
|
|
|
|
use (only to be created) that context in particular (for further
|
|
|
|
|
operations it will use the context of the thread). But, a method with
|
|
|
|
|
what name?
|
|
|
|
|
|
|
|
|
|
Tim Peters proposes three methods to create from diverse sources
|
|
|
|
|
(from_string, from_int, from_float). I proposed to use one method,
|
|
|
|
|
``create_decimal()``, without caring about the data type. Michael
|
|
|
|
|
Chermside: "The name just fits my brain. The fact that it uses the
|
|
|
|
|
context is obvious from the fact that it's Context method".
|
|
|
|
|
|
|
|
|
|
The community agreed with that. I think that it's OK because a newbie
|
|
|
|
|
will not be using the creation method from Context (the separate
|
|
|
|
|
method in Decimal to construct from float is just to prevent newbies
|
|
|
|
|
from encountering binary floating point issues).
|
|
|
|
|
|
|
|
|
|
So, in short, if you want to create a Decimal instance using a
|
|
|
|
|
particular context (that will be used just at creation time and not
|
|
|
|
|
any further), you'll have to use a method of that context::
|
|
|
|
|
|
|
|
|
|
# n is any datatype accepted in Decimal(n) plus float
|
|
|
|
|
mycontext.create_decimal(n)
|
|
|
|
|
|
|
|
|
|
Example::
|
|
|
|
|
|
|
|
|
|
>>> # create a standard decimal instance
|
|
|
|
|
>>> Decimal("11.2233445566778899")
|
|
|
|
|
Decimal( (0, (1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9), -16) )
|
|
|
|
|
>>>
|
|
|
|
|
>>> # create a decimal instance using the thread context
|
|
|
|
|
>>> thread_context = getcontext()
|
|
|
|
|
>>> thread_context.prec
|
|
|
|
|
9
|
|
|
|
|
>>> thread_contex.create_decimal("11.2233445566778899")
|
|
|
|
|
Decimal( (0, (1, 1, 2, 2, 3, 3, 4, 4, 6), -7L) )
|
|
|
|
|
>>>
|
|
|
|
|
>>> # create a decimal instance using other context
|
|
|
|
|
>>> other_context = thread_context.copy()
|
|
|
|
|
>>> other_context.prec = 4
|
|
|
|
|
>>> other_context.create_decimal("11.2233445566778899")
|
|
|
|
|
Decimal( (0, (1, 1, 2, 2), -2L) )
|
2004-01-29 14:59:56 -05:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Implicit construction
|
|
|
|
|
---------------------
|
|
|
|
|
|
|
|
|
|
As the implicit construction is the consequence of an operation, it
|
|
|
|
|
will be affected by the context as is detailed in each point.
|
|
|
|
|
|
|
|
|
|
John Roth suggested that "The other type should be handled in the same
|
|
|
|
|
way the decimal() constructor would handle it". But Alex Martelli
|
|
|
|
|
thinks that
|
|
|
|
|
|
|
|
|
|
this total breach with Python tradition would be a terrible
|
|
|
|
|
mistake. 23+"43" is NOT handled in the same way as 23+int("45"),
|
|
|
|
|
and a VERY good thing that is too. It's a completely different
|
|
|
|
|
thing for a user to EXPLICITLY indicate they want construction
|
|
|
|
|
(conversion) and to just happen to sum two objects one of which by
|
|
|
|
|
mistake could be a string.
|
|
|
|
|
|
|
|
|
|
So, here I define the behaviour again for each data type.
|
|
|
|
|
|
|
|
|
|
|
2004-03-31 11:24:00 -05:00
|
|
|
|
From int or long
|
|
|
|
|
''''''''''''''''
|
|
|
|
|
|
2004-06-30 00:46:47 -04:00
|
|
|
|
An int or long is a treated like a Decimal explicitly constructed from
|
|
|
|
|
Decimal(str(x)) in the current context (meaning that the to-string rules
|
|
|
|
|
for rounding are applied and the appropriate flags are set). This
|
|
|
|
|
guarantees that expressions like ``Decimal('1234567') + 13579`` match
|
|
|
|
|
the mental model of ``Decimal('1234567') + Decimal('13579')``. That
|
|
|
|
|
model works because all integers are representable as strings without
|
|
|
|
|
representation error.
|
2004-03-31 11:24:00 -05:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
From string
|
|
|
|
|
'''''''''''
|
|
|
|
|
|
|
|
|
|
Everybody agrees to raise an exception here.
|
2004-01-29 14:59:56 -05:00
|
|
|
|
|
2004-03-31 11:24:00 -05:00
|
|
|
|
|
|
|
|
|
From float
|
|
|
|
|
''''''''''
|
|
|
|
|
|
|
|
|
|
Aahz is strongly opposed to interact with float, suggesting an
|
|
|
|
|
explicit conversion:
|
2004-01-29 14:59:56 -05:00
|
|
|
|
|
|
|
|
|
The problem is that Decimal is capable of greater precision,
|
|
|
|
|
accuracy, and range than float.
|
|
|
|
|
|
2004-06-29 23:37:13 -04:00
|
|
|
|
The example of the valid python expression, ``35 + 1.1``, seems to suggest
|
|
|
|
|
that ``Decimal(35) + 1.1`` should also be valid. However, a closer look
|
|
|
|
|
shows that it only demonstrates the feasibility of integer to floating
|
|
|
|
|
point conversions. Hence, the correct analog for decimal floating point
|
|
|
|
|
is ``35 + Decimal(1.1)``. Both coercions, int-to-float and int-to-Decimal,
|
|
|
|
|
can be done without incurring representation error.
|
2004-01-29 14:59:56 -05:00
|
|
|
|
|
2004-06-29 23:37:13 -04:00
|
|
|
|
The question of how to coerce between binary and decimal floating point
|
|
|
|
|
is more complex. I proposed allowing the interaction with float,
|
2004-01-29 14:59:56 -05:00
|
|
|
|
making an exact conversion and raising ValueError if exceeds the
|
|
|
|
|
precision in the current context (this is maybe too tricky, because
|
|
|
|
|
for example with a precision of 9, ``Decimal(35) + 1.2`` is OK but
|
|
|
|
|
``Decimal(35) + 1.1`` raises an error).
|
|
|
|
|
|
2004-03-31 11:24:00 -05:00
|
|
|
|
This resulted to be too tricky. So tricky, that c.l.p agreed to raise
|
|
|
|
|
TypeError in this case: you could not mix Decimal and float.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
From Decimal
|
|
|
|
|
''''''''''''
|
|
|
|
|
|
|
|
|
|
There isn't any issue here.
|
2004-01-29 14:59:56 -05:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Use of Context
|
|
|
|
|
--------------
|
|
|
|
|
|
|
|
|
|
In the last pre-PEP I said that "The Context must be omnipresent,
|
|
|
|
|
meaning that changes to it affects all the current and future Decimal
|
|
|
|
|
instances". I was wrong. In response, John Roth said:
|
|
|
|
|
|
|
|
|
|
The context should be selectable for the particular usage. That
|
|
|
|
|
is, it should be possible to have several different contexts in
|
|
|
|
|
play at one time in an application.
|
|
|
|
|
|
|
|
|
|
In comp.lang.python, Aahz explained that the idea is to have a
|
|
|
|
|
"context per thread". So, all the instances of a thread belongs to a
|
|
|
|
|
context, and you can change a context in thread A (and the behaviour
|
|
|
|
|
of the instances of that thread) without changing nothing in thread B.
|
|
|
|
|
|
|
|
|
|
Also, and again correcting me, he said:
|
|
|
|
|
|
|
|
|
|
(the) Context applies only to operations, not to Decimal
|
|
|
|
|
instances; changing the Context does not affect existing instances
|
|
|
|
|
if there are no operations on them.
|
|
|
|
|
|
|
|
|
|
Arguing about special cases when there's need to perform operations
|
|
|
|
|
with other rules that those of the current context, Tim Peters said
|
|
|
|
|
that the context will have the operations as methods. This way, the
|
|
|
|
|
user "can create whatever private context object(s) it needs, and
|
|
|
|
|
spell arithmetic as explicit method calls on its private context
|
|
|
|
|
object(s), so that the default thread context object is neither
|
|
|
|
|
consulted nor modified".
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Python Usability
|
|
|
|
|
----------------
|
|
|
|
|
|
|
|
|
|
- Decimal should support the basic arithmetic (``+, -, *, /, //, **,
|
|
|
|
|
%, divmod``) and comparison (``==, !=, <, >, <=, >=, cmp``)
|
|
|
|
|
operators in the following cases (check `Implicit Construction`_ to
|
|
|
|
|
see what types could OtherType be, and what happens in each case):
|
|
|
|
|
|
|
|
|
|
- Decimal op Decimal
|
|
|
|
|
- Decimal op otherType
|
|
|
|
|
- otherType op Decimal
|
|
|
|
|
- Decimal op= Decimal
|
|
|
|
|
- Decimal op= otherType
|
|
|
|
|
|
|
|
|
|
- Decimal should support unary operators (``-, +, abs``).
|
|
|
|
|
|
2004-06-16 10:27:00 -04:00
|
|
|
|
- repr() should round trip, meaning that::
|
|
|
|
|
|
|
|
|
|
m = Decimal(...)
|
|
|
|
|
m == eval(repr(m))
|
|
|
|
|
|
|
|
|
|
- Decimal should be immutable.
|
|
|
|
|
|
2004-01-29 14:59:56 -05:00
|
|
|
|
- Decimal should support the built-in methods:
|
|
|
|
|
|
|
|
|
|
- min, max
|
|
|
|
|
- float, int, long
|
|
|
|
|
- str, repr
|
|
|
|
|
- hash
|
|
|
|
|
- bool (0 is false, otherwise true)
|
|
|
|
|
|
2004-06-16 10:27:00 -04:00
|
|
|
|
There's been some discussion in python-dev about the behaviour of
|
|
|
|
|
``hash()``. The community agrees that if the values are the same, the
|
|
|
|
|
hashes of those values should also be the same. So, while Decimal(25)
|
|
|
|
|
== 25 is True, hash(Decimal(25)) should be equal to hash(25).
|
2004-01-29 14:59:56 -05:00
|
|
|
|
|
2004-06-16 10:27:00 -04:00
|
|
|
|
The detail is that you can NOT compare Decimal to floats or strings,
|
|
|
|
|
so we should not worry about them giving the same hashes. In short::
|
2004-01-29 14:59:56 -05:00
|
|
|
|
|
2004-06-16 10:27:00 -04:00
|
|
|
|
hash(n) == hash(Decimal(n)) # Only if n is int, long, or Decimal
|
|
|
|
|
|
|
|
|
|
Regarding str() and repr() behaviour, Ka-Ping Yee proposes that repr()
|
|
|
|
|
have the same behaviour as str() and Tim Peters proposes that str()
|
|
|
|
|
behave like the to-scientific-string operation from the Spec.
|
|
|
|
|
|
|
|
|
|
This is posible, because (from Aahz): "The string form already
|
|
|
|
|
contains all the necessary information to reconstruct a Decimal
|
|
|
|
|
object".
|
|
|
|
|
|
|
|
|
|
And it also complies with the Spec; Tim Peters:
|
|
|
|
|
|
|
|
|
|
There's no requirement to have a method *named* "to_sci_string",
|
|
|
|
|
the only requirement is that *some* way to spell to-sci-string's
|
|
|
|
|
functionality be supplied. The meaning of to-sci-string is
|
|
|
|
|
precisely specified by the standard, and is a good choice for both
|
|
|
|
|
str(Decimal) and repr(Decimal).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Documentation
|
|
|
|
|
=============
|
|
|
|
|
|
|
|
|
|
This section explains all the public methods and attributes of Decimal
|
|
|
|
|
and Context.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Decimal Attributes
|
|
|
|
|
------------------
|
|
|
|
|
|
|
|
|
|
Decimal has no public attributes. The internal information is stored
|
|
|
|
|
in slots and should not be accessed by end users.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Decimal Methods
|
|
|
|
|
---------------
|
|
|
|
|
|
|
|
|
|
Following are the conversion and arithmetic operations defined in the
|
|
|
|
|
Spec, and how that functionality can be achieved with the actual
|
|
|
|
|
implementation.
|
|
|
|
|
|
|
|
|
|
- to-scientific-string: Use builtin function ``str()``::
|
|
|
|
|
|
|
|
|
|
>>> d = Decimal('123456789012.345')
|
|
|
|
|
>>> str(d)
|
|
|
|
|
'1.23456789E+11'
|
|
|
|
|
|
|
|
|
|
- to-engineering-string: Use method ``to_eng_string()``::
|
|
|
|
|
|
|
|
|
|
>>> d = Decimal('123456789012.345')
|
|
|
|
|
>>> d.to_eng_string()
|
|
|
|
|
'123.456789E+9'
|
|
|
|
|
|
|
|
|
|
- to-number: Use Context method ``create_decimal()``. The standard
|
|
|
|
|
constructor or ``from_float()`` constructor cannot be used because
|
|
|
|
|
these do not use the context (as is specified in the Spec for this
|
|
|
|
|
conversion).
|
|
|
|
|
|
|
|
|
|
- abs: Use builtin function ``abs()``::
|
|
|
|
|
|
|
|
|
|
>>> d = Decimal('-15.67')
|
|
|
|
|
>>> abs(d)
|
|
|
|
|
Decimal('15.67')
|
|
|
|
|
|
|
|
|
|
- add: Use operator ``+``::
|
|
|
|
|
|
|
|
|
|
>>> d = Decimal('15.6')
|
|
|
|
|
>>> d + 8
|
|
|
|
|
Decimal('23.6')
|
|
|
|
|
|
|
|
|
|
- subtract: Use operator ``-``::
|
|
|
|
|
|
|
|
|
|
>>> d = Decimal('15.6')
|
|
|
|
|
>>> d - 8
|
|
|
|
|
Decimal('7.6')
|
|
|
|
|
|
|
|
|
|
- compare: Use method ``compare()``. This method (and not the
|
|
|
|
|
built-in function cmp()) should only be used when dealing with
|
|
|
|
|
*special values*::
|
|
|
|
|
|
|
|
|
|
>>> d = Decimal('-15.67')
|
|
|
|
|
>>> nan = Decimal('NaN')
|
|
|
|
|
>>> d.compare(23)
|
|
|
|
|
'-1'
|
|
|
|
|
>>> d.compare(nan)
|
|
|
|
|
'NaN'
|
|
|
|
|
>>> cmp(d, 23)
|
|
|
|
|
-1
|
|
|
|
|
>>> cmp(d, nan)
|
|
|
|
|
1
|
|
|
|
|
|
|
|
|
|
- divide: Use operator ``/``::
|
|
|
|
|
|
|
|
|
|
>>> d = Decimal('-15.67')
|
|
|
|
|
>>> d / 2
|
|
|
|
|
Decimal('-7.835')
|
|
|
|
|
|
|
|
|
|
- divide-integer: Use operator ``//``::
|
|
|
|
|
|
|
|
|
|
>>> d = Decimal('-15.67')
|
|
|
|
|
>>> d // 2
|
|
|
|
|
Decimal('-7')
|
|
|
|
|
|
|
|
|
|
- max: Use method ``max()``. Only use this method (and not the
|
|
|
|
|
built-in function max()) when dealing with *special values*::
|
|
|
|
|
|
|
|
|
|
>>> d = Decimal('15')
|
|
|
|
|
>>> nan = Decimal('NaN')
|
|
|
|
|
>>> d.max(8)
|
|
|
|
|
Decimal('15')
|
|
|
|
|
>>> d.max(nan)
|
|
|
|
|
Decimal('NaN')
|
|
|
|
|
|
|
|
|
|
- min: Use method ``min()``. Only use this method (and not the
|
|
|
|
|
built-in function min()) when dealing with *special values*::
|
|
|
|
|
|
|
|
|
|
>>> d = Decimal('15')
|
|
|
|
|
>>> nan = Decimal('NaN')
|
|
|
|
|
>>> d.min(8)
|
|
|
|
|
Decimal('8')
|
|
|
|
|
>>> d.min(nan)
|
|
|
|
|
Decimal('NaN')
|
|
|
|
|
|
|
|
|
|
- minus: Use unary operator ``-``::
|
|
|
|
|
|
|
|
|
|
>>> d = Decimal('-15.67')
|
|
|
|
|
>>> -d
|
|
|
|
|
Decimal('15.67')
|
|
|
|
|
|
|
|
|
|
- plus: Use unary operator ``+``::
|
|
|
|
|
|
|
|
|
|
>>> d = Decimal('-15.67')
|
|
|
|
|
>>> +d
|
|
|
|
|
Decimal('-15.67')
|
|
|
|
|
|
|
|
|
|
- multiply: Use operator ``*``::
|
|
|
|
|
|
|
|
|
|
>>> d = Decimal('5.7')
|
|
|
|
|
>>> d * 3
|
|
|
|
|
Decimal('17.1')
|
|
|
|
|
|
|
|
|
|
- normalize: Use method ``normalize()``::
|
|
|
|
|
|
|
|
|
|
>>> d = Decimal('123.45000')
|
|
|
|
|
>>> d.normalize()
|
|
|
|
|
Decimal('123.45')
|
|
|
|
|
>>> d = Decimal('120.00')
|
|
|
|
|
>>> d.normalize()
|
|
|
|
|
Decimal('1.2E+2')
|
|
|
|
|
|
|
|
|
|
- quantize: Use method ``quantize()``::
|
|
|
|
|
|
|
|
|
|
>>> d = Decimal('2.17')
|
|
|
|
|
>>> d.quantize(Decimal('0.001'))
|
|
|
|
|
Decimal('2.170')
|
|
|
|
|
>>> d.quantize(Decimal('0.1'))
|
|
|
|
|
Decimal('2.2')
|
|
|
|
|
|
|
|
|
|
- remainder: Use operator ``%``::
|
|
|
|
|
|
|
|
|
|
>>> d = Decimal('10')
|
|
|
|
|
>>> d % 3
|
|
|
|
|
Decimal('1')
|
|
|
|
|
>>> d % 6
|
|
|
|
|
Decimal('4')
|
|
|
|
|
|
|
|
|
|
- remainder-near: Use method ``remainder_near()``::
|
|
|
|
|
|
|
|
|
|
>>> d = Decimal('10')
|
|
|
|
|
>>> d.remainder_near(3)
|
|
|
|
|
Decimal('1')
|
|
|
|
|
>>> d.remainder_near(6)
|
|
|
|
|
Decimal('-2')
|
|
|
|
|
|
|
|
|
|
- round-to-integral-value: Use method ``to_integral()``::
|
|
|
|
|
|
|
|
|
|
>>> d = Decimal('-123.456')
|
|
|
|
|
>>> d.to_integral()
|
|
|
|
|
Decimal('-123')
|
|
|
|
|
|
|
|
|
|
- same-quantum: Use method ``same_quantum()``::
|
|
|
|
|
|
|
|
|
|
>>> d = Decimal('123.456')
|
|
|
|
|
>>> d.same_quantum(Decimal('0.001'))
|
|
|
|
|
True
|
|
|
|
|
>>> d.same_quantum(Decimal('0.01'))
|
|
|
|
|
False
|
|
|
|
|
|
|
|
|
|
- square-root: Use method ``sqrt()``::
|
|
|
|
|
|
|
|
|
|
>>> d = Decimal('123.456')
|
|
|
|
|
>>> d.sqrt()
|
|
|
|
|
Decimal('11.1110756')
|
|
|
|
|
|
|
|
|
|
- power: User operator ``**``::
|
|
|
|
|
|
|
|
|
|
>>> d = Decimal('12.56')
|
|
|
|
|
>>> d ** 2
|
|
|
|
|
Decimal('157.7536')
|
|
|
|
|
|
|
|
|
|
Following are other methods and why they exist:
|
|
|
|
|
|
|
|
|
|
- ``adjusted()``: Returns the adjusted exponent. This concept is
|
|
|
|
|
defined in the Spec: the adjusted exponent is the value of the
|
|
|
|
|
exponent of a number when that number is expressed as though in
|
|
|
|
|
scientific notation with one digit before any decimal point::
|
|
|
|
|
|
|
|
|
|
>>> d = Decimal('12.56')
|
|
|
|
|
>>> d.adjusted()
|
|
|
|
|
1
|
|
|
|
|
|
|
|
|
|
- ``from_float()``: Class method to create instances from float data
|
|
|
|
|
types::
|
|
|
|
|
|
|
|
|
|
>>> d = Decimal.from_float(12.35)
|
|
|
|
|
>>> d
|
|
|
|
|
Decimal('12.3500000')
|
|
|
|
|
|
|
|
|
|
- ``as_tuple()``: Show the internal structure of the Decimal, the
|
|
|
|
|
triple tuple. This method is not required by the Spec, but Tim
|
|
|
|
|
Peters proposed it and the community agreed to have it (it's useful
|
|
|
|
|
for developing and debugging)::
|
|
|
|
|
|
|
|
|
|
>>> d = Decimal('123.4')
|
|
|
|
|
>>> d.as_tuple()
|
|
|
|
|
(0, (1, 2, 3, 4), -1)
|
|
|
|
|
>>> d = Decimal('-2.34e5')
|
|
|
|
|
>>> d.as_tuple()
|
|
|
|
|
(1, (2, 3, 4), 3)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Context Attributes
|
|
|
|
|
------------------
|
|
|
|
|
|
|
|
|
|
These are the attributes that can be changed to modify the context.
|
|
|
|
|
|
|
|
|
|
- ``prec`` (int): the precision::
|
|
|
|
|
|
|
|
|
|
>>> c.prec
|
|
|
|
|
9
|
|
|
|
|
|
|
|
|
|
- ``rounding`` (str): rounding type (how to round)::
|
|
|
|
|
|
|
|
|
|
>>> c.rounding
|
|
|
|
|
'half_even'
|
|
|
|
|
|
|
|
|
|
- ``trap_enablers`` (dict): if trap_enablers[exception] = 1, then an
|
|
|
|
|
exception is raised when it is caused::
|
|
|
|
|
|
|
|
|
|
>>> c.trap_enablers[Underflow]
|
|
|
|
|
0
|
|
|
|
|
>>> c.trap_enablers[Clamped]
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
|
- ``flags`` (dict): when an exception is caused, flags[exception] is
|
|
|
|
|
incremented (whether or not the trap_enabler is set). Should be
|
|
|
|
|
reset by the user of Decimal instance::
|
|
|
|
|
|
|
|
|
|
>>> c.flags[Underflow]
|
|
|
|
|
0
|
|
|
|
|
>>> c.flags[Clamped]
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
|
- ``Emin`` (int): minimum exponent::
|
|
|
|
|
|
|
|
|
|
>>> c.Emin
|
|
|
|
|
-999999999
|
|
|
|
|
|
|
|
|
|
- ``Emax`` (int): maximum exponent::
|
|
|
|
|
|
|
|
|
|
>>> c.Emax
|
|
|
|
|
999999999
|
|
|
|
|
|
|
|
|
|
- ``capitals`` (int): boolean flag to use 'E' (True/1) or 'e'
|
|
|
|
|
(False/0) in the string (for example, '1.32e+2' or '1.32E+2')::
|
|
|
|
|
|
|
|
|
|
>>> c.capitals
|
|
|
|
|
1
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Context Methods
|
|
|
|
|
---------------
|
|
|
|
|
|
|
|
|
|
The following methods comply with Decimal functionality from the Spec.
|
|
|
|
|
Be aware that the operations that are called through a specific
|
|
|
|
|
context use that context and not the thread context.
|
|
|
|
|
|
|
|
|
|
To use these methods, take note that the syntax changes when the
|
|
|
|
|
operator is binary or unary, for example::
|
|
|
|
|
|
|
|
|
|
>>> mycontext.abs(Decimal('-2'))
|
|
|
|
|
'2'
|
|
|
|
|
>>> mycontext.multiply(Decimal('2.3'), 5)
|
|
|
|
|
'11.5'
|
|
|
|
|
|
|
|
|
|
So, the following are the Spec operations and conversions and how to
|
|
|
|
|
achieve them through a context (where ``d`` is a Decimal instance and
|
|
|
|
|
``n`` a number that can be used in an `Implicit construction`_):
|
|
|
|
|
|
|
|
|
|
- to-scientific-string: ``to_sci_string(d)``
|
|
|
|
|
- to-engineering-string: ``to_eng_string(d)``
|
|
|
|
|
- to-number: ``create_decimal(number)``, see `Explicit construction`_
|
|
|
|
|
for ``number``.
|
|
|
|
|
- abs: ``abs(d)``
|
|
|
|
|
- add: ``add(d, n)``
|
|
|
|
|
- subtract: ``subtract(d, n)``
|
|
|
|
|
- compare: ``compare(d, n)``
|
|
|
|
|
- divide: ``divide(d, n)``
|
|
|
|
|
- divide-integer: ``divide_int(d, n)``
|
|
|
|
|
- max: ``max(d, n)``
|
|
|
|
|
- min: ``min(d, n)``
|
|
|
|
|
- minus: ``minus(d)``
|
|
|
|
|
- plus: ``plus(d)``
|
|
|
|
|
- multiply: ``multiply(d, n)``
|
|
|
|
|
- normalize: ``normalize(d)``
|
|
|
|
|
- quantize: ``quantize(d, d)``
|
|
|
|
|
- remainder: ``remainder(d)``
|
|
|
|
|
- remainder-near: ``remainder_near(d)``
|
|
|
|
|
- round-to-integral-value: ``to_integral(d)``
|
|
|
|
|
- same-quantum: ``same_quantum(d, d)``
|
|
|
|
|
- square-root: ``sqrt(d)``
|
|
|
|
|
- power: ``power(d, n)``
|
|
|
|
|
|
|
|
|
|
The following methods are to support decimal functionality through
|
|
|
|
|
Context:
|
|
|
|
|
|
|
|
|
|
- ``divmod(d, n)``
|
|
|
|
|
- ``eq(d, d)``
|
|
|
|
|
- ``gt(d, d)``
|
|
|
|
|
- ``lt(d, d)``
|
|
|
|
|
|
|
|
|
|
These are methods that return useful information from the Context:
|
|
|
|
|
|
|
|
|
|
- ``Etiny()``: Minimum exponent considering precision.
|
|
|
|
|
|
|
|
|
|
>>> c.Emin
|
|
|
|
|
-999999999
|
|
|
|
|
>>> c.Etiny()
|
|
|
|
|
-1000000007
|
|
|
|
|
|
|
|
|
|
- ``Etop()``: Maximum exponent considering precision.
|
|
|
|
|
|
|
|
|
|
>>> c.Emax
|
|
|
|
|
999999999
|
|
|
|
|
>>> c.Etop()
|
|
|
|
|
999999991
|
|
|
|
|
|
|
|
|
|
- ``copy()``: Returns a copy of the context.
|
2004-01-29 14:59:56 -05:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Reference Implementation
|
|
|
|
|
========================
|
|
|
|
|
|
|
|
|
|
To be included later:
|
|
|
|
|
|
|
|
|
|
- code
|
|
|
|
|
- test code
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
References
|
|
|
|
|
==========
|
|
|
|
|
|
|
|
|
|
.. [1] ANSI standard X3.274-1996 (Programming Language REXX):
|
|
|
|
|
http://www.rexxla.org/Standards/ansi.html
|
|
|
|
|
|
|
|
|
|
.. [2] General Decimal Arithmetic specification (Cowlishaw):
|
|
|
|
|
http://www2.hursley.ibm.com/decimal/decarith.html (related
|
|
|
|
|
documents and links at http://www2.hursley.ibm.com/decimal/)
|
|
|
|
|
|
|
|
|
|
.. [3] ANSI/IEEE standard 854-1987 (Radix-Independent Floating-Point
|
|
|
|
|
Arithmetic):
|
|
|
|
|
http://www.cs.berkeley.edu/~ejr/projects/754/private/drafts/854-1987/dir.html
|
|
|
|
|
(unofficial text; official copies can be ordered from
|
|
|
|
|
http://standards.ieee.org/catalog/ordering.html)
|
|
|
|
|
|
|
|
|
|
.. [4] Tim Peter's FixedPoint at SourceForge:
|
|
|
|
|
http://fixedpoint.sourceforge.net/
|
|
|
|
|
|
|
|
|
|
.. [5] IEEE 754 revision:
|
|
|
|
|
http://grouper.ieee.org/groups/754/revision.html
|
|
|
|
|
|
|
|
|
|
.. [6] IEEE 754 references:
|
|
|
|
|
http://babbage.cs.qc.edu/courses/cs341/IEEE-754references.html
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Copyright
|
|
|
|
|
=========
|
|
|
|
|
|
|
|
|
|
This document has been placed in the public domain.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
..
|
|
|
|
|
Local Variables:
|
|
|
|
|
mode: indented-text
|
|
|
|
|
indent-tabs-mode: nil
|
|
|
|
|
sentence-end-double-space: t
|
|
|
|
|
fill-column: 70
|
|
|
|
|
End:
|