tags:

views:

134

answers:

4

Recently, a correspondent mentioned float.as_integer_ratio(), new in Python 2.6, noting that typical floating point implementations are essentially rational approximations of real numbers. Intrigued, I had to try π:

>>> float.as_integer_ratio(math.pi);
(884279719003555L, 281474976710656L)

I was mildly surprised not to see the more accurate result due to Arima,:

(428224593349304L, 136308121570117L)

For example, this code:

#! /usr/bin/env python
from decimal import *
getcontext().prec = 36
print "python: ",Decimal(884279719003555) / Decimal(281474976710656)
print "Arima:  ",Decimal(428224593349304) / Decimal(136308121570117)
print "Wiki:    3.14159265358979323846264338327950288"

produces this output:

python:  3.14159265358979311599796346854418516
Arima:   3.14159265358979323846264338327569743
Wiki:    3.14159265358979323846264338327950288

Certainly, the result is correct given the precision afforded by 64-bit floating-point numbers, but it leads me to ask: How can I find out more about the implementation limitations of as_integer_ratio()? Thanks for any guidance.

Additional links: Stern-Brocot, Python source, IronPython source.

+1  A: 

Go through the source repository for Python, find out who committed the code of interest, and send them off an email.

Ignacio Vazquez-Abrams
I hope I won't have to go that far! :-) For reference: http://svn.python.org/view/python/trunk/Objects/floatobject.c?view=log
trashgod
+2  A: 

The algorithm used by as_integer_ratio only considers powers of 2 in the denominator. Here is a (probably) better algorithm.

Victor Liu
trashgod
Saying the algorithm is not accurate is a flawed explanation. `float.as_integer_ratio()` simply returns you a (numerator, denominator) pair which is *rigorously equal* to the floating-point number in question (that's why the denominator is a power of two, since standard floating-point numbers have a base-2 exponent). The loss in accuracy comes from the floating-point representation itself, *not* from float.as_integer_ratio() which is actually lossless.
Antoine P.
IIUC, the algorithm is sufficiently accurate for a given floating-point precision. The genesis of the denominator is what puzzled me. The algorithm would never produce Arima's unique result, and there would be no point given the required precision.
trashgod
+2  A: 

May I recommend gmpy's implementation of the Stern-Brocot tree:

>>> import gmpy
>>> import math
>>> gmpy.mpq(math.pi)
mpq(245850922,78256779)
>>> x=_
>>> float(x)
3.1415926535897931
>>> 

again, the result is "correct within the precision of 64-bit floats" (53-bit "so-called" mantissas;-), but:

>>> 245850922 + 78256779
324107701
>>> 884279719003555 + 281474976710656
1165754695714211L
>>> 428224593349304L + 136308121570117
564532714919421L

...gmpy's precision is obtained so much cheaper (in terms of sum of numerator and denominator values) than Arima's, much less Python 2.6's!-)

Alex Martelli
I see the benefit. I've used GMP from Ada before, so `gmpy` will be handy. http://code.google.com/p/adabindinggmpmpfr/
trashgod
+1  A: 

You get better approximations using

fractions.Fraction.from_float(math.pi).limit_denominator()

Fractions are included since maybe version 3.0. However, math.pi doesn't have enough accuracy to return a 30 digit approximation.

fesno