Je me demandais comment convertir un nombre décimal en une fraction dans sa forme la plus basse en Python.
Par exemple:
0.25 -> 1/4
0.5 -> 1/2
1.25 -> 5/4
3 -> 3/1
Vous avez deux options:
Utilisez float.as_integer_ratio()
:
>>> (0.25).as_integer_ratio()
(1, 4)
(à partir de Python 3.6, vous pouvez faites de même avec un objet decimal.Decimal()
_) .)
Utilisez le type fractions.Fraction()
_ :
>>> from fractions import Fraction
>>> Fraction(0.25)
Fraction(1, 4)
Ce dernier a une conversion très utile str()
:
>>> str(Fraction(0.25))
'1/4'
>>> print Fraction(0.25)
1/4
Comme les valeurs en virgule flottante peuvent être imprécises, vous pouvez vous retrouver avec des fractions "étranges"; limite le dénominateur à 'simplifier' quelque peu la fraction, avec Fraction.limit_denominator()
:
>>> Fraction(0.185)
Fraction(3332663724254167, 18014398509481984)
>>> Fraction(0.185).limit_denominator()
Fraction(37, 200)
Si vous utilisez toujours Python 2.6, alors Fraction()
] ne prend pas encore en charge le passage direct d'un float
, mais vous peut combiner les deux techniques ci-dessus en:
Fraction(*0.25.as_integer_ratio())
Ou vous pouvez simplement utiliser la méthode Fraction.from_float()
class :
Fraction.from_float(0.25)
qui fait essentiellement la même chose, par ex. prenez le ratio entier Tuple et transmettez-le sous forme de deux arguments distincts.
Et une petite démo avec vos exemples de valeurs:
>>> for f in (0.25, 0.5, 1.25, 3.0):
... print f.as_integer_ratio()
... print repr(Fraction(f)), Fraction(f)
...
(1, 4)
Fraction(1, 4) 1/4
(1, 2)
Fraction(1, 2) 1/2
(5, 4)
Fraction(5, 4) 5/4
(3, 1)
Fraction(3, 1) 3
Le module fractions
et la méthode float.as_integer_ratio()
sont nouveaux dans Python 2.6.
from fractions import Fraction
print(Fraction(0.25))
print(Fraction(0.5))
print(Fraction(1.25))
print(Fraction(3))
#1/4
#1/2
#5/4
#3
Si vous souhaitez imprimer un fraction appropriée, cette petite recette devrait faire:
from fractions import Fraction
def dec_to_proper_frac(dec):
sign = "-" if dec < 0 else ""
frac = Fraction(abs(dec))
return (f"{sign}{frac.numerator // frac.denominator} "
f"{frac.numerator % frac.denominator}/{frac.denominator}")
Cela va imprimer comme suit:
>>> dec_to_proper_frac(3.75)
>>> "3 3/4"
Pour développer Martijn Pieters excellente réponse avec une option supplémentaire en raison de l'imprécision inhérente à des flotteurs plus complexes. Par exemple:
>>> f = 0.8857097
>>> f.as_integer_ratio()
(1994440937439217, 2251799813685248) # mathematically wrong
>>> Fraction(f)
Fraction(1994440937439217, 2251799813685248) # same result but in a class
>>> Fraction(f).limit_denominator()
Fraction(871913, 984423) # still imprecise
Le résultat mathématique souhaité était 8857097/10000000
qui peut être obtenu en convertissant une chaîne puis en la manipulant.
Réponse modifiée
J'ai trouvé un moyen beaucoup plus simple de résoudre le problème de précision.
>>> Fraction(str(f))
Fraction(8857097, 10000000)
La conversion en chaîne permet également des instances Decimal précises
>>> Decimal(f).as_integer_ratio()
(1994440937439217, 2251799813685248)
>>> Decimal(str(f)).as_integer_ratio()
(8857097, 10000000)
Réponse d'origine
def float_to_ratio(flt):
if int(flt) == flt: # to prevent 3.0 -> 30/10
return int(flt), 1
flt_str = str(flt)
flt_split = flt_str.split('.')
numerator = int(''.join(flt_split))
denominator = 10 ** len(flt_split[1])
return numerator, denominator
Maintenant testons-le:
>>> float_to_ratio(f)
(8857097, 10000000) # mathematically correct
Je noterai que ce type de précision de fraction n’est pas optimisé et ne sera généralement pas nécessaire, mais il est ici complet. Cette fonction ne simplifie pas la fraction, mais vous pouvez effectuer un traitement supplémentaire pour la réduire:
>>> n = 0.5
>>> float_to_ratio(n)
(5, 10)
>>> Fraction(*float_to_ratio(n))
Fraction(1, 2)