# prePEP: Decimal data type

John Roth newsgroups at jhrothjr.com
Sat Nov 1 17:40:36 CET 2003

```"Paul Moore" <pf_moore at yahoo.co.uk> wrote in message
news:3cd884n4.fsf at yahoo.co.uk...
> "John Roth" <newsgroups at jhrothjr.com> writes:
>
> >> The idea is to have a Decimal data type, for every use where decimals
are
> >> needed but floating point is too inexact.
> >>
> >> The Decimal data type should support the Python standard functions and
> >> operations and must comply the decimal arithmetic ANSI standard
> >> X3.274-1996.
> >
> > Why is ANSI 274 significant? The reason I ask this is that this is
> > a ***floating point*** standard, and I don't think that we particularly
> > care for decimal floating point.
>
> To be honest, I have little if any practical experience with numeric
> representation issues, but it seems sensible to me to implement a
> pre-existing, and presumably well thought out, standard, rather than
> inventing something ad-hoc.
>
> Of course, if the need is for something other than what ANSI 274
> standardises, which seems to be what you are implying, then fair
> enough. But what do you require? Infinite-precision decimal
> arithmetic? If so, can you explain how you'd handle something like
> 1/3?

As I said in the response to Alex, division is the one place where
fixed decimal gets into trouble. To repeat what I said to him,
I'd eliminate the division operators completely, and replace them
with a div(dividend, divisor, [resultplaces], [roundingpolicy])
operator.

The division operators make a hidden assumption that they
know what you want. That's ok for floating point, and it's
inherent for rationals, but it doesn't really work for integers
or fixed decimal.

In the spirit of explicit is better than implicit, I'd rather have
the control inherent in a div() operator.

>
> (I'm not being deliberately awkward here - my impression is that
> representation issues are *hard*, and there are a lot of traps you can
> fall into by oversimplifying. That's why I prefer the idea of a
> pre-existing standard: it implies that *someone* has thought about the
> hard stuff).
>
> > Floating point presumes limited precision. In other words, if the actual
> > number (exclusive of the location of the decimal point) gets too large,
> > the least significant part is ... thrown away. I don't want that.
> >
> > Since we've got infinite precision integer arithmetic, going to
> > limited precision decimal arithmetic is, IMNSHO, a step backwards.
>
> Even infinite precision integers throw away information, in some
> sense. Witness:
>
> >>> 1L//3L
> 0L

Same comment. Integer division, as it's currently implemented, is
simply wrong. However, we had that discussion and decided to
take one of the several different approaches, flying in the face
of the evidence that any choice was not going to be useable in
some context.

When you try to simplify an inherently complex situation by
putting a pretty face on it, all you do is confuse the issue more.

>
> >> When passing floating point to the constructor, what should happen?
> >>
> >>     j. ``Decimal(1.1) == Decimal('1.1')``
> >>     k. ``Decimal(1.1) ==
> >> Decimal('110000000000000008881784197001252...e-51')``
> >
> > Clearly, j is the correct answer. It's not all that hard to do, either.
>
> No way. Consider:
>
> >>> 1.1
> 1.1000000000000001
> >>> 1.1==1.1000000000000001
> True
>
> So what should Decimal(1.1000000000000001) evaluate to? It can't be
> Decimal('1.1'), as that contradicts your statement that j "clearly"
> applies. But it *also* can't be Decimal('1.1000000000000001'), as then
> we have the *same number* converting to two *different* Decimal
> values.
>
> As I say, it's hard.

Not that hard. 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. That's how printing libraries
handle the issue, after all.

You can also special case that with some lightweight compiler
magic. All that really has to happen is that the lexer has to pass
the 1.1 to the compiler without converting it to a float first,
then the parser can apply a special rule when it sees that token
in the context of decimal(1.1).

> I'd probably support Decimal(float) giving an exception, on the basis
> that if you're doing this, you probably don't know what you're getting
> into :-) Having a special method, say Decimal.round_float(f, digits),
> is probably OK, though...

I think someone earlier suggested (in the context of the Money type)
having the number of digits be an optional arguement to the constructor.
That is: decimal(1.1, 1) showing one place after the decimal point.

However, I prefer having the compiler take care of it.

John Roth
>
> Paul.
> --
> This signature intentionally left blank

```