1-0.95

Steven D'Aprano steve+comp.lang.python at pearwood.info
Thu Jul 3 04:19:30 CEST 2014


On Wed, 02 Jul 2014 23:00:15 +0300, Marko Rauhamaa wrote:

> Steven D'Aprano <steve+comp.lang.python at pearwood.info>:
> 
>>>    >>> Rational(2).sqrt() * Rational(2).sqrt() == Rational(2)
>>>    False
>>
>> Square root of 2 is not a rational number.
> 
> Nobody said it was. 

Your comment can be read as implying it. You stated:

    [quote]
    Even "arbitrary-precision" RATIONALS [emphasis added] would 
    suffer from the same problem 
    [end quote]

and then showed an invented example where you squared a NON-RATIONAL. By 
the way, there's no need to use an invented example. Here is an actual 
example:

py> import math
py> from fractions import Fraction
py> math.sqrt(Fraction(2))**2
2.0000000000000004


> It's just that even "arbitrary-precision" rational
> numbers wouldn't free you from the issues of floating-point numbers.

Hmmm, well, I wouldn't put it that way. I would put it that a rational 
number class has problems of its own. A correct, non-buggy rational 
number class does not suffer from most of the problems of floating point 
numbers. For example, apart from x = 0, the following:

    1/(1/x) == x

is always true in mathematics, and always true with a rational class, but 
not always true with floating point:

py> x = 49.0
py> 1/(1/x) == x
False

py> x = Fraction(49)
py> 1/(1/x) == x
True


The specific problem you show, with sqrt, comes about because it takes 
you outside of the rationals and into the floating point numbers.


> The Decimal number class won't do it, either, of course.

Decimals are floating point number, so they suffer from the same kind of 
failures as other floating point numbers.


> On the other hand, floating-point numbers are perfect whenever you deal
> with science and measurement. 

/head-desk

I'm sorry Marko, have you not being paying attention? Have you ever done 
any numeric programming? Floating-point is *hard*, not "perfect". Even 
*trivially simple* arithmetic problems can burn you, badly. Have you not 
heard of catastrophic cancellation, or the Table Maker's Dilemma, or ill-
conditioned equations? If scientists don't have to worry about these 
things (and they actually do), it's because the people writing the 
scientific libraries have worried about them.

Almost every thing that most non-experts believe is true about doing 
calculations on a computer is false -- including, I daresay, me. I'm sure 
I've probably got some wrong ideas too. Or at least incomplete ones.

This page gives some examples:

http://introcs.cs.princeton.edu/java/91float/

including a calculation of the harmonic sum 1/1 + 1/2 + 1/3 + 1/4 + ... 
Mathematically that sum diverges to infinity; numerically, it converges 
to a fixed, finite value.


> And when you deal with business (= money),
> integers are the obvious choice.

Unfortunately there is nothing obvious about using integers. If you want 
to represent $1.01, what could be more obvious than using 1.01? But 
that's the *wrong solution*.

Unfortunately using integers for money is trickier than you may think. If 
all you're doing is multiplying, adding and subtracting, then using 101 
for $1.01 is fine. But as soon as you start doing division, percentages, 
sales tax calculations, interest calculations, currency conversions, 
etc., you've got a problem. How do you divide 107 cents by 3? If you just 
truncate:

py> (107//3)*3
105

you've just lost two cents. If you round to the nearest whole number:

py> (round(107/3))*3
108

you've just invented a cent from thin air. Both answers are wrong. 
Depending on your application, you may pick one or the other, but either 
way, you have to care about rounding, and that's neither obvious nor easy.


> I would venture to say that the real applications for Decimal are very
> rare. In practice, I'm afraid, people with rather a weak understanding
> of numbers and computation might gravitate toward Decimal unnecessarily.

Financial applications are one of the real applications for Decimal. You 
can think of Decimal numbers as an easy way to fake the use of integers, 
without having to worry about moving the decimal point around or come up 
with your own rounding modes.

py> from decimal import *
py> setcontext(Context(prec=3))
py> (Decimal("1.07")/3)*3
Decimal('1.07')


-- 
Steven



More information about the Python-list mailing list