Some notes about float approximations in mxNumber
Kay Schluehr
kay.schluehr at gmx.net
Fri Apr 1 14:04:40 EST 2005
Hi Mark,
I was a bit surprised to find the very slow Farey approximation by
means of the <FareyRational> class in the mxNumber package. If the goal
was to reconstruct a rational from a float it is not a good choice and
should be replaced by a continued fractions approximation. Some time
ago I implemented it by myself so it can be published here:
def cfrac(z,bound=10**-5,n=10):
'''
creates a continued fraction from some number z.
@ bound - terminate cf if the rest is lower than the provided bound
@ n - terminate cf after n steps
'''
l = []
while 1:
a = int(z)
l.append(a)
y = z-a
if y<=bound or len(l)==n:
return l
z = 1./y
def fold(cf):
'''
create u/v = cfrac(a0,a1,...,an) using following rules:
1. / u1 u0 \ / a1 1 \ / a0 1 \
| | = | | * | |
\ v1 v0 / \ 1 0 / \ 1 0 /
2. The recursion rules
v(n+1) = v(n)*a(n+1)+v(n-1)
u(n+1) = u(n)*a(n+1)+u(n-1)
'''
if len(cf)<2:
return Rational(0,1)
un = cf[0]*cf[1]+1
vn = cf[1]
un_1 = cf[0]
vn_1 = 1
for a in cf[2:]:
b = un
un = un*a+un_1
un_1 = b
b = vn
vn = vn*a+vn_1
vn_1 = b
return Rational(un,vn)
>>> fold(cfrac(1./3))
1/3
>>> fold(cfract(1525/42.))
1525/42
import math
>>> fold(cfract(math.sqrt(2)))
3363/2378
It is possible to provide this functionality in an even more efficient
manner because it is usefull to bound only the approximation error of
the rational, not the size of the continued fraction.
def float2ratio(z, bound=10**-5):
'''
convert a float into a Rational.
The approximation is bound by the error-limit 'bound'.
'''
a = int(z)
y = z-a
z = 1./y
b = int(z)
un = a*b+1
vn = b
un_1 = a
vn_1 = 1
a = b
while 1:
y = z-a
if y<bound:
return Rational(un,vn),k
z = 1./y
a = int(z)
x = un
un = un*a+un_1
un_1 = x
x = vn
vn = vn*a+vn_1
vn_1 = x
xn = float(un)/vn
yn = float(un_1)/vn_1
if abs(xn-yn)<=bound:
return Rational(un,vn)
>>> math.sqrt(2)
1.4142135623730951
>>> float2ratio(math.sqrt(2))
1393/985
>>> 1393./985
1.4142131979695431
^
>>> float2ratio(math.sqrt(2),10**-10)
275807/195025
>>> 275807./195025
1.4142135623637995
^
>>> math.pi
3.1415926535897931
>>> float2ratio(math.pi,10**-14)
245850922/78256779
>>> 245850922./78256779
3.1415926535897931
Note that the algorithm needed only 13 iteration steps to approximate
pi
in this accuracy.
Regards,
Kay
More information about the Python-list
mailing list