On Thu, Mar 6, 2014 at 6:51 PM, Mark H. Harris <harrismh777@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.