2010-01-10 37 views
6

¿Hay una implementación python pura de fractions.Fraction que admita long s como numerador y denominador? Desafortunadamente, la exponenciación parece estar codificada para devolver un float (¡ack!), Que al menos debería ser compatible con decimal.Decimal.Fracciones con precisión decimal

Si no lo hay, supongo que probablemente pueda hacer una copia de la biblioteca e intentar reemplazar las ocurrencias de float() con algo apropiado de Decimal, pero preferiría algo que otros hayan probado anteriormente.

Aquí está un ejemplo de código:

base = Fraction.from_decimal(Decimal(1).exp()) 
a = Fraction(69885L, 53L) 
x = Fraction(9L, 10L) 

print base**(-a*x), type(base**(-a*x)) 

resultados en 0.0 <type 'float'> donde la respuesta debe ser una muy pequeña decimal.

Actualización: Tengo la siguiente solución alternativa por ahora (asumiendo, para a ** b, que ambas son fracciones, por supuesto, necesitaré otra función cuando exp_ sea una carroza o sea ella misma un decimal):

def fracpow(base, exp_): 
    base = Decimal(base.numerator)/Decimal(base.denominator) 
    exp_ = Decimal(exp_.numerator)/Decimal(exp_.denominator) 

    return base**exp_ 

que da la respuesta 4.08569925773896097019795484811E-516.

Todavía estaría interesado si hay una mejor manera de hacer esto sin las funciones adicionales (supongo que si trabajo con la clase Fraction lo suficiente, encontraré otras carrozas trabajando en mis resultados).

+0

Cuando hago fracciones.Fraccion (11,17) ** 2 Recibo fracciones.Fraccion (121, 289). ¿Qué obtienes? –

+0

try type (fractions.Fraction (11,17) ** 2.1) – Noah

+0

IMO, el comportamiento que ves es el correcto, aunque no el que quieres.Como el exponente es un flotante, es razonable que las cosas se conviertan en flotación. Ahora, si el exponente fuera Fracción (21,10), sería más interesante ... –

Respuesta

6

"elevar a una potencia" no es una operación cerrada sobre los racionales (a diferencia de los habituales cuatro operaciones aritméticas): No hay número racional r tal que r == 2 ** 0.5. La leyenda dice que Pitágoras (de cuyo teorema sigue este hecho tan simplemente) había matado a su discípulo Hippasus por el horrible crimen de probar esto; parece que simpatizas con la supuesta reacción de Pitágoras ;-), dado tu extraño uso de "debería".

Las fracciones de Python están destinadas a ser exactas, por lo que inevitablemente hay casos en los que aumentar una fracción a la potencia de otra fracción será absolutamente incapaz de para devolver una fracción como resultado; y "debería" simplemente no puede aplicarse con sensatez a una imposibilidad matemática.

Así que lo mejor que puedes hacer es aproximadamente para obtener el resultado deseado, p. obteniendo un resultado que no es una fracción exacta (los flotadores generalmente se consideran suficientes para el propósito) y luego aproximándolo nuevamente con una fracción. La mayoría de las implementaciones Pure Python existentes (hay muchos archivosrationals.py encontrados en la red ;-) prefieren no implementar un operador **, ¡pero desde luego no hay nada que te impida tomar una decisión de diseño diferente en tu propia implementación!)

+0

Esto es por supuesto correcto matemáticamente, pero la razón por la que estoy usando la clase Fraction es porque quiero una alta precisión numérica, y estoy tratando de actualizar el código alguien más escribió usando la biblioteca clnum. Como Fraction está feliz de tomar un largo como numerador o denominador, parece justo que sea capaz de devolver un decimal al exponenciarse. ¡Ahora, si solo pudiera descubrir si fracpow (0,0) se debe definir como 1, o si eso fue un error en el código! – Noah

+1

@Noah, no puedo ayudarte con el código Python puro aquí, ya que para tales tareas siempre uso 'gmpy', por supuesto; pero gmpy.mpq tampoco permite raíces inexactas con exponentes fraccionarios (mi decisión de diseño en este caso), así que pasaría por flotadores de alta precisión (gmpy.mpf en mi caso) como lo harías con Decimal (que también son flotantes, solo decimales en lugar de binarios, con implementaciones SW en lugar de HW, y con una precisión configurable tan alta como se desee - gmpy.mpf son similares, pero binarios en lugar de decimales). –

+0

El error de su aproximación numérica de exp será generalmente más grande que el error al aproximar el resultado de la aproximación con un flotante. –

0

Puede escribir su propia función "pow" para las fracciones que no usan la exponenciación de coma flotante. ¿Es eso lo que intentas hacer?

Esto aumentará una fracción a una potencia entera al volver a flotar.

def pow(fract, exp): 
    if exp == 0: 
     return fract 
    elif exp % 2 == 0: 
     t = pow(fract, exp//2) 
     return t*t 
    else: 
     return fract*pos(fract, exp-1) 
+0

Hmmm, mejor tener _some_ caso base para terminar la recursión eventualmente! -) –

Cuestiones relacionadas