Some notes about float approximations in mxNumber
M.-A. Lemburg
mal at egenix.com
Mon Apr 4 09:59:26 EDT 2005
Kay Schluehr wrote:
> Hi Marc,
>
> 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.
The idea was to be able to create a Rational() from a float
using a given upper bound on the denominator.
The standard Rational() constructor already provides a way
to construct a rational out of a Python or mxNumber float,
but always uses maximum precision - which may not always be
what the user wants (e.g. to work around rounding errors).
Thanks for posting your faster version. I think it would be
a good candidate for a Python Cookbook Entry and I'll
see whether I can add something like this to one of the next
mxNumber releases.
> 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
>
--
Marc-Andre Lemburg
eGenix.com
Professional Python Services directly from the Source (#1, Apr 04 2005)
>>> Python/Zope Consulting and Support ... http://www.egenix.com/
>>> mxODBC.Zope.Database.Adapter ... http://zope.egenix.com/
>>> mxODBC, mxDateTime, mxTextTools ... http://python.egenix.com/
________________________________________________________________________
::: Try mxODBC.Zope.DA for Windows,Linux,Solaris,FreeBSD for free ! ::::
More information about the Python-list
mailing list