Join GitHub today
GitHub is home to over 50 million developers working together to host and review code, manage projects, and build software together.
Sign up| PEP: 327 | |
| Title: Decimal Data Type | |
| Version: $Revision$ | |
| Last-Modified: $Date$ | |
| Author: Facundo Batista <facundo@taniquetil.com.ar> | |
| Status: Final | |
| Type: Standards Track | |
| Content-Type: text/x-rst | |
| Created: 17-Oct-2003 | |
| Python-Version: 2.4 | |
| Post-History: 30-Nov-2003, 02-Jan-2004, 29-Jan-2004 | |
| 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 | |
| operations, and must comply with the decimal arithmetic ANSI standard | |
| 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 | |
| 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. | |
| This work is based on code and test functions written by Eric Price, | |
| Aahz and Tim Peters. Just before Python 2.4a1, the decimal.py | |
| `reference implementation`_ was moved into the standard library; along | |
| with the documentation and the test suite, this was the work of | |
| Raymond Hettinger. Much of the explanation in this PEP is taken from | |
| Cowlishaw's work [2]_, comp.lang.python and python-dev. | |
| Motivation | |
| ========== | |
| Here I'll expose the reasons of why I think a Decimal data type is | |
| needed and why other numeric data types are not enough. | |
| 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 | |
| someone already thought out all the creepy cases for you. And to a | |
| 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 | |
| 123456 ==> 12346e1 | |
| (note that in the last line the number got rounded to fit in five digits). | |
| 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 | |
| and the 3 as numerator and denominator?", which takes us to the next | |
| point. | |
| Why not rational? | |
| ----------------- | |
| 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://speleotrove.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). | |
| 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, | |
| but definitely the idea is not to get a context per Decimal number. | |
| 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. | |
| These definitions don't affect the internal storage of the Decimal | |
| numbers, just the way that the arithmetic operations are performed. | |
| The context is mainly defined by the following parameters (see | |
| `Context Attributes`_ for all context attributes): | |
| - Precision: The maximum number of significant digits that can result | |
| from an arithmetic operation (integer > 0). There is no maximum for | |
| this value. | |
| - 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 | |
| - precision: is set to 9 | |
| - 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]_ | |
| Division by zero division-by-zero [sign,inf] | |
| Inexact inexact unchanged | |
| 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]_ | |
| ==================== ================= =================================== | |
| 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 possible:: | |
| >>> d1 = Decimal("1e999999999") # at the exponent limit | |
| >>> d1 | |
| Decimal("1E+999999999") | |
| >>> d1 * 10 # exceed the limit, got infinity | |
| Traceback (most recent call last): | |
| File "<pyshell#3>", line 1, in ? | |
| d1 * 10 | |
| ... | |
| ... | |
| Overflow: above Emax | |
| >>> getcontext().Emax = 1000000000 # increase the limit | |
| >>> d1 * 10 # does not exceed any more | |
| Decimal("1.0E+1000000000") | |
| >>> d1 * 100 # exceed again | |
| Traceback (most recent call last): | |
| File "<pyshell#3>", line 1, in ? | |
| d1 * 100 | |
| ... | |
| ... | |
| Overflow: above Emax | |
| 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 | |
| incremented by 1 (round toward positive infinity):: | |
| 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 | |
| value of the result is incremented by 1 (round toward negative | |
| infinity):: | |
| 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 | |
| **very large** suite of test cases. | |
| 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 | |
| affects just operations' results. The only exception to this is when | |
| you're `Creating from Context`_. | |
| From int or long | |
| '''''''''''''''' | |
| There's no loss and no need to specify any other information:: | |
| Decimal(35) | |
| Decimal(-124) | |
| From string | |
| ''''''''''' | |
| 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):: | |
| Decimal("-12") | |
| Decimal("23.2e-7") | |
| Also, you can construct in this way all special values (Infinity and | |
| Not a Number):: | |
| Decimal("Inf") | |
| Decimal("NaN") | |
| From float | |
| '''''''''' | |
| The initial discussion on this item was what should | |
| 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 | |
| ``Decimal('110000000000000008881784197001252...e-51')``, why can't I | |
| 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. | |
| Anyway, Paul Moore showed that (1) can't work, because:: | |
| (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 | |
| constructor and mwilson agreed:: | |
| 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. | |
| 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:: | |
| 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:: | |
| 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'). | |
| 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 floats | |
| and decimals are lacking. Java included float/decimal conversions to handle | |
| an obscure case where calculations are best performed in decimal even though | |
| 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. | |
| From tuples | |
| ''''''''''' | |
| Aahz suggested to construct from tuples: it's easier | |
| 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 | |
| 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 | |
| From Decimal | |
| '''''''''''' | |
| No mystery here, just a copy. | |
| Syntax for All Cases | |
| '''''''''''''''''''' | |
| :: | |
| Decimal(value1) | |
| 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"). | |
| 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("11.2233445566778899") | |
| >>> | |
| >>> # create a decimal instance using the thread context | |
| >>> thread_context = getcontext() | |
| >>> thread_context.prec | |
| 28 | |
| >>> thread_context.create_decimal("11.2233445566778899") | |
| Decimal("11.2233445566778899") | |
| >>> | |
| >>> # create a decimal instance using other context | |
| >>> other_context = thread_context.copy() | |
| >>> other_context.prec = 4 | |
| >>> other_context.create_decimal("11.2233445566778899") | |
| Decimal("11.22") | |
| 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. | |
| From int or long | |
| '''''''''''''''' | |
| 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. | |
| From string | |
| ''''''''''' | |
| Everybody agrees to raise an exception here. | |
| From float | |
| '''''''''' | |
| Aahz is strongly opposed to interact with float, suggesting an | |
| explicit conversion: | |
| The problem is that Decimal is capable of greater precision, | |
| accuracy, and range than float. | |
| 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. | |
| The question of how to coerce between binary and decimal floating point | |
| is more complex. I proposed allowing the interaction with float, | |
| 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). | |
| 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. | |
| 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``). | |
| - repr() should round trip, meaning that:: | |
| m = Decimal(...) | |
| m == eval(repr(m)) | |
| - Decimal should be immutable. | |
| - Decimal should support the built-in methods: | |
| - min, max | |
| - float, int, long | |
| - str, repr | |
| - hash | |
| - bool (0 is false, otherwise true) | |
| 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). | |
| 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:: | |
| 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 possible, 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 ``divmod(d, n)`` method supports decimal functionality through | |
| Context. | |
| 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. | |
| Reference Implementation | |
| ======================== | |
| As of Python 2.4-alpha, the code has been checked into the standard | |
| library. The latest version is available from: | |
| http://svn.python.org/view/python/trunk/Lib/decimal.py | |
| The test cases are here: | |
| http://svn.python.org/view/python/trunk/Lib/test/test_decimal.py | |
| References | |
| ========== | |
| .. [1] ANSI standard X3.274-1996 (Programming Language REXX): | |
| http://www.rexxla.org/Standards/ansi.html | |
| .. [2] General Decimal Arithmetic specification (Cowlishaw): | |
| http://speleotrove.com/decimal/decarith.html (related | |
| documents and links at http://speleotrove.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: |