Welcome, guest | Sign In | My Account | Store | Cart

Find integer ratio approximations to floating point numbers.

Python, 35 lines
 ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35``` ```def approx1(c, maxd): 'Slow way using sequential search' d = min(xrange(2, maxd), key=lambda d: abs(round(c*d)/d/c-1)) return int(round(c*d)), d import fractions # Available in Py2.6 and Py3.0 def approx2(c, maxd): 'Fast way using continued fractions' return fractions.Fraction.from_float(c).limit_denominator(maxd) if __name__ == '__main__': import math for x in (math.pi, math.e): for d in (10, 100, 1000, 10000, 100000, 1000000): print approx1(x, d), approx2(x, d) print ##### Sample output (22, 7) 22/7 (311, 99) 311/99 (355, 113) 355/113 (355, 113) 355/113 (312689, 99532) 312689/99532 (3126535, 995207) 3126535/995207 (19, 7) 19/7 (193, 71) 193/71 (1457, 536) 1457/536 (25946, 9545) 25946/9545 (271801, 99990) 271801/99990 (1084483, 398959) 1084483/398959 ```

355/113 is the best approximation to Pi with a denominator below 10000. 1457/536 is the best approximation to E with a denominator below 1000. Eduardo Moguillansky 13 years, 10 months ago

pi should probably be c Alexey Marinichev 13 years, 8 months ago

Continued fractions work better for this, wikipedia has an article on those. Using continued fractions you can always restore the rational number given its floating point approximation, if its denominator is not too big. Just truncate the fraction if its term gets unreasonably large.

Below is the implementation that is almost always correct, except for those special cases when you must not accept exact half of the last term.

``````# Generate continued fraction for x.
def chain(x):
while True:
a = int(x)
x = 1/(x-a)
yield a

# Get best rational approximations of a continued fraction.
def eval_chain(f):
n1, n2 = 1, 0
d1, d2 = 0, 1
for a in f:
for b in xrange((a+1)/2, a+1):
yield (b*n1+n2, b*d1+d2)
n1, n2 = a*n1+n2, n1
d1, d2 = a*d1+d2, d1

def approx(c, maxd):
n2, d2 = int(c), 1
for n1, d1 in eval_chain(chain(c)):
if d1 > maxd:
return n2, d2
n2, d2 = n1, d1
`````` Created by Raymond Hettinger on Wed, 9 Jan 2008 (PSF)

### Required Modules

• (none specified)