[Python-Dev] Decimal issues - Conclusion
FBatista at uniFON.com.ar
Fri Jun 4 12:08:26 EDT 2004
Sorry for the delay, but between the moment that the discussion in
python-dev settled down and now, my car got stolen, my grandmother
got into the hospital (brain accident), and I was designed as
Deployment Manager of the new SMS Center that bought the company I
Anyway, all the following is the discussion itself post-crunched.
The first three items are the result of the subjects for which the
discussion was initiated. The other items are subjects that arose
A lot of new functionalities were asked for inclusion into the PEP,
but we're trying to keep it pure at this stage. From Tim Peters:
Piles of "convenience features" should wait until people actually
use this in real life, so can judge what's truly clumsy based on
experience instead of speculation.
Unless there're strong feelings against any point, my plan is to
include the results in the PEP 327 and send it again to Tim for a
re-review. The only tricky part is that I propose something new at
the end of the first point.
Thanks you all for the time and knowledge that invested in this.
Even the points that didn't got through to this resume added a lot of
The original post asked if to keep the maximum exponent value::
DEFAULT_MAX_EXPONENT = 999999999
DEFAULT_MIN_EXPONENT = -999999999
ABSOLUTE_MAX_EXP = 999999999
ABSOLUTE_MIN_EXP = -999999999
The general consenss 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
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
Edward Loper give us an example of when the limits are to be crossed:
Said that, Robert Brewer and Andrew Lentvorski want the limits to be
easily modifiable by the users. Actually, this is quite posible::
>>> d1 = Decimal("1e999999999") # in the exponent limit
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') )
However, note that sometimes absolute maximum and minimum are
actually used, and they can not be changed. Actually, I want to
As long as all the good effects of a maximum can be achieved with
modifiable maximum and minimum, I propose to not have absolute
ones (in the Spec this is optional).
This point was about the behaviour of hash() on Decimals.
Community agrees that if the values are the same, also the hashes of
those values. 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 to give the same hashes.
hash(n) == hash(Decimal(n)) # Only if n is int, long, or Decimal
There were three operations that I missed to put in the PEP:
- ``to-scientific-string``: Converts a number to a string using
scientific notation if an exponent is needed.
- ``to-engineering-string``: Converts a number to a string using
engineering notation if an exponent is needed.
- ``to-number``: Converts a string to a number.
The discussion originated the community wish to change the behaviour
of repr() and str(). Ka-Ping Yee proposes that repr() have the same
behaviour that str() and Tim Peters proposes to str() behave like the
to-scientific-string operation specified in the Spec.
This is posible, because (from Aahz): "The string form already
contains all the necessary information to reconstruct a Decimal
And 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).
Tim also proposes a method as_tuple() to return the internal triple
tuple, the way repr() behaved until now.
So, in short:
- str() and repr() will behave as the to-scientific-string operation.
Because of that we won't put a specific method for it.
- ``to_eng_string`` will be the name of the method for the
- As long as we can pass a string to create a Decimal using the
context, we won't put a specific method for the to-number operation.
- We'll put a new method to return the internal triple tuple: as_tuple()
Create with other Context
This item arose from two sources in parallel. Ka-Ping Yee proposes
to pass the context as argument at construct time (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
receives a parameter "honour_context" in True (I don't like it,
because the doc specify to honor the context and I don't want the
method to comply with the specification regarding the value of an
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
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
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::
it will be::
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 (fur further
operations it'll use the context of the thread). But, a method with
I proposed the name create_decimal(). Tim Peters proposes three
methods to create from diverse sources::
Michael Chermside says to use the same method without caring about
the data type: "The name just fits my brain. The fact that it uses
the context is obvious from the fact that it's Context method". 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 binary floating point issues).
So, in short, if you want to create a Decimal instance using a
particular context (that will be used just in creation time and not
further), you'll have to use a method of that context::
mycontext.create_decimal(n) # being n any datatype accepted in
# Decimal(n) plus float.
>>> # create a standard decimal instance
Decimal( (0, (1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9),
>>> # create a decimal instance using the thread context
>>> thread_context = getcontext()
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
Decimal( (0, (1, 1, 2, 2), -2L) )
New method in Context
Paul Moore wants a copy method in Context, so I'll put copy() and
__copy__ methods in Context (but not in Decimal: it's immutable, it's
Extracting a method from Decimal
Tim Peters doesn't want round() because it's not in the Spec and you
can achieve the same result with quantize()::
>>> d = Decimal("12345.6789")
>>> dimes = Decimal('0.1')
>>> print d.quantize(dimes)
>>> print d.quantize(Decimal('1e1'))
So, as long the PEP is for implementing the Spec, I'll drop round().
More information about the Python-Dev