[Python-ideas] Python Numbers as Human Concept Decimal System

David Mertz mertz at gnosis.cx
Fri Mar 7 10:17:26 CET 2014


On Thu, Mar 6, 2014 at 6:51 PM, Mark H. Harris <harrismh777 at gmail.com>wrote:

> I hate this:
> >>> from decimal import Decimal
> >>> a=Decimal(1)
> >>> b=Decimal(.1)
> >>> a+b
> Decimal('1.100000000000000005551115123')
> >>>
> ... and I think the people who DO know intimately how python works, should
> do something to fix it.
>

The problem here--and the problem with the "default number type is decimal"
is that what you want fixed is "unfixable."  As a number of other people
have noted, what you are demanding is that numbers have finite sized and
exact representations, which they simply don't and can't in computers with
finite memory.

Your example, Mark, is merely one where you have misspelled your Python
code:

>>> from decimal import Decimal as D
>>> D('1') + D('.1')
Decimal('1.1')

Fix your spelling error, and in this case you get what you want.  However,
what about this:

>>> D(1/3) * 3
Decimal('0.9999999999999999444888487687')

There's no way to fix that spelling.  Yes, you might be able to play with a
decimal.Context in a way that gets this one example to happen to round the
way you want it to, but no context will do so generically for every similar
expected identity under reciprocal operators.

By coincidence, this works under binary FB *because* of the limited
precision of floats:

>>> 1/3 * 3
1.0

But that's just luck, basically, it's not because we've managed to
precisely represent 1/3 as a binary float.  We don't have to look all that
far for a problem case:

>>> 1/49 * 49
0.9999999999999999

Of course, we do have Fractions, which will always do the right thing under
the reciprocal division and multiplication operators:

>>> F('1/3') * 3
Fraction(1, 1)

Don't misspell this either, of course:

>>> F(1/3) * 3
Fraction(18014398509481983, 18014398509481984)

However, even if Fraction were to become the default numeric type for
Python, and preserve the reciprocity of div/mul, that wouldn't help us any
under the hoped reciprocals of sqrt() and square. Similarly, of course, for
trigonometric identities and various other numeric functions that produce
irrational answers. E.g.:

>>> from fractions import Fraction as F
>>> sqrt(F('1/3'))**2
0.3333333333333333
>>> F(sqrt(F('1/3'))**2)
Fraction(6004799503160661, 18014398509481984)

Well, that answer is a binary floating point; we leave Fraction land pretty
easily in Python.  The float we get, of course, is something that's not
exactly 1/3 (but is pretty close).

Even if we imagined some future version of Python that did sqrt() purely as
a Fraction without converting to IEEE 754, we STILL couldn't ever have an
exact identity.  There simply does not exist any Fraction that can
accurately represent sqrt(F('1/3')); there are only rational numbers that
are "pretty close."  Maybe this one, for example:

>>> F(sqrt(F('1/3')))
Fraction(1300077228592327, 2251799813685248)

Maybe this hypothetical Python 4000 that didn't do the conversion to
floating point would produce a different approximation, but it would always
be an approximation, not the Real (irrational) answer.



-- 
Keeping medicines from the bloodstreams of the sick; food
from the bellies of the hungry; books from the hands of the
uneducated; technology from the underdeveloped; and putting
advocates of freedom in prisons.  Intellectual property is
to the 21st century what the slave trade was to the 16th.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-ideas/attachments/20140307/f5546c95/attachment-0001.html>


More information about the Python-ideas mailing list