Limitaciones de implementación de float.as_integer_ratio ()

Recientemente, un corresponsal mencionó float.as_integer_ratio() , nuevo en Python 2.6, señalando que las implementaciones típicas de punto flotante son esencialmente aproximaciones racionales de números reales. Intrigado, tuve que probar π:

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

Me sorprendió un poco no ver el resultado más preciso debido a Arima :

 (428224593349304L, 136308121570117L) 

Por ejemplo, este código:

 #! /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" 

produce esta salida:

 python: 3.14159265358979311599796346854418516
 Arima: 3.14159265358979323846264338327569743
 Wiki: 3.14159265358979323846264338327950288

Ciertamente, el resultado es correcto dada la precisión que ofrecen los números de punto flotante de 64 bits, pero me lleva a preguntar: ¿Cómo puedo obtener más información sobre las limitaciones de implementación de as_integer_ratio() ? Gracias por cualquier orientación.

Enlaces adicionales: Stern-Brocot tree y Python source .

El algoritmo utilizado por as_integer_ratio solo considera potencias de 2 en el denominador . Aquí hay un (probablemente) mejor algoritmo .

Puedo recomendar la implementación de gmpy del árbol Stern-Brocot :

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

de nuevo, el resultado es “correcto dentro de la precisión de las flotaciones de 64 bits” (las llamadas “mantissas 😉 de 53 bits, pero:

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

… la precisión de gmpy se obtiene mucho más barata (en términos de sum de valores de numerador y denominador) que la de Arima, y ​​mucho menos Python 2.6’s! -)

Obtienes mejores aproximaciones usando

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

Las fracciones están incluidas ya que tal vez la versión 3.0. Sin embargo, math.pi no tiene la precisión suficiente para devolver una aproximación de 30 dígitos.