2010-01-16 8 views
8

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, que tenía que probar π:Limitaciones de implementación de float.as_integer_ratio()

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

yo estaba un poco sorprendido de no ver más accurate resultado 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 

Sin duda, el resultado es correcto dada la precisión que brindan los números de coma 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.

Otros enlaces: Stern-Brocot tree y Python source.

+2

La respuesta aceptada es engañosa. El método 'as_integer_ratio' devuelve el numerador y el denominador de una fracción cuyo valor _exactly_ coincide con el valor del número de coma flotante que se le pasa. Si desea una representación perfectamente precisa de su flotador como una fracción, use 'as_integer_ratio'. Si desea una _aproximación simplificada_ con un denominador y un numerador más pequeños, investigue 'fractions.Fraction.limit_denominator'. IOW, 'math.pi' es una aproximación a π. Pero '884279719003555/281474976710656' no es una aproximación a' math.pi'; es exactamente igual a eso. –

+0

@MarkDickinson: Su punto está bien; aclara esta [respuesta relacionada] (https://stackoverflow.com/a/2076903/230513). Aunque la [respuesta aceptada] (https://stackoverflow.com/a/2076296/230513) podría usar algo de mantenimiento, me ayudó a ver dónde mi pensamiento había salido mal. – trashgod

Respuesta

3

El algoritmo utilizado por as_integer_ratio solo considers powers of 2 in the denominator. Aquí hay un (probablemente) better algorithm.

+0

Aha, '281474976710656 = 2^48'. Ahora veo de dónde vienen los valores. Interesante para comparar implementaciones: http: //svn.python.org/view/python/trunk/Objects/floatobject.c? revision = 77139 & view = markup – trashgod

+6

Decir que el algoritmo no es exacto es una explicación errónea. 'float.as_integer_ratio()' simplemente le devuelve un par (numerador, denominador) que es * rigurosamente igual * al número de coma flotante en cuestión (es por eso que el denominador es una potencia de dos, ya que los números de coma flotante estándar tienen una exponente de base-2). La pérdida de precisión proviene de la representación en coma flotante en sí, * no * de float.as_integer_ratio() que en realidad no tiene pérdida. –

+0

IIUC, el algoritmo es lo suficientemente preciso para una precisión dada de punto flotante. La génesis del denominador es lo que me dejó perplejo. El algoritmo nunca produciría el resultado único de Arima, y ​​no tendría sentido dada la precisión requerida. – trashgod

1

Acceda al repositorio fuente de Python, descubra quién ha enviado el código de interés y envíelo por correo electrónico.

3

Te recomendamos gmpy 's aplicación de la Stern-Brocot tree:

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

vez más, el resultado es "correcto dentro de la precisión de los flotadores 64 bits" (53-bit "llamados" mantisas; -), pero:

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

... precisión de gmpy se obtiene mucho más barato (en términos de suma de los valores de numerador y denominador) que Arima, y ​​mucho menos de Python 2.6 -)

!
+0

Veo el beneficio. He usado GMP de Ada antes, así que 'gmpy' será útil. http://code.google.com/p/adabindinggmpmpfr/ – trashgod

3

a obtener mejores aproximaciones usando

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

fracciones se incluyen desde tal vez la versión 3.0. Sin embargo, math.pi no tiene suficiente precisión para devolver una aproximación de 30 dígitos.