How about adding rational fraction to Python?
Steven D'Aprano
steve at REMOVE-THIS-cybersource.com.au
Sat Feb 16 22:19:27 EST 2008
On Sat, 16 Feb 2008 18:21:40 -0800, Carl Banks wrote:
> Consider what happens when you add two fractions:
>
> 1/2 + 1/5
>
> To do that, you have to take the LCD of the denomintor, in this case 10,
> so you get
>
> 5/10 + 2/10 = 7/10
>
> Now imagine that you're adding a lot of different numbers with a lot of
> different bases. That LCD's going to be pretty big.
It *will* be pretty big, or it *could be* pretty big?
The worst case comes from something like this:
a/2 + b/3 + c/4 + d/5 + e/6 + ...
where the denominator could be as high as n! (for n = the number of
fractions you're adding). Since n! gets large quickly, you don't want
that.
But a less-naive implementation shouldn't be that bad: the lowest common
denominator of a/2 + c/4 is 4, not 8. Still, multiplying all the
relatively-prime denominators will still get large quickly, just not
quite as quickly as n!.
Which is where a good fraction implementation should allow you to specify
the largest denominator you wish to see. After all, the difference
between:
975328755018/6827301285133
and
1/7
is less than 2e-13, or a relative error of approximately 0.0000000001%.
If you care about a difference of that magnitude, you're probably already
using numpy.
An interesting question would be, how large a denominator would you need
to beat the precision of floats? My back-of-the-envelope calculation
suggests that limiting your denominator to 4503599627370496 is enough to
give you a precision as good as floats:
# on my PC, the machine accuracy is 2.2204460492503131e-16
# this is the smallest float which, when added to 1.0,
# doesn't give 1.0
>>> eps = 2.2204460492503131e-16
>>> 1/eps
4503599627370496.0
The advantage is that while 1000.0+eps gives 1000.0 (which it shouldn't),
(1000*4503599627370496 + 1)/4503599627370496 is a perfectly reasonable
fraction to work with. Ugly to the human eye perhaps, but if your PC is
grinding to a halt on such a fraction, maybe you should take this as a
sign that 64K *isn't* enough memory for everybody.
*wink*
[snip]
> The thing I don't like about rationals is that they give a false sense
> of security. They are performing reasonably, and then you make a slight
> change or some circumstance changes slightly and suddenly they blow up.
Or, to put it another way, rationals and floats both are dangerous, but
in different ways. The performance of rationals can fall drastically, and
floating point calculations can suddenly become inaccurate. You make your
choice and take your chances.
--
Steven
More information about the Python-list
mailing list