Wie konvertiere ich eine Dezimalzahl in einen Bruch?

76

Ich habe mich gefragt, wie man in Python eine Dezimalstelle in einen Bruch in ihrer niedrigsten Form konvertiert.

Zum Beispiel:

0.25  -> 1/4
0.5   -> 1/2
1.25  -> 5/4
3     -> 3/1
user2370460
quelle
0,25 in 25/100 umrechnen, dann den größten gemeinsamen Faktor herausfinden?
Lev Levitsky

Antworten:

136

Sie haben zwei Möglichkeiten:

  1. Verwendung float.as_integer_ratio():

    >>> (0.25).as_integer_ratio()
    (1, 4)
    

    (Ab Python 3.6 können Sie dasselbe mit einem decimal.Decimal()Objekt tun .)

  2. Verwenden Sie den fractions.Fraction()Typ :

    >>> from fractions import Fraction
    >>> Fraction(0.25)
    Fraction(1, 4)
    

Letzteres hat eine sehr hilfreiche str()Konvertierung:

>>> str(Fraction(0.25))
'1/4'
>>> print Fraction(0.25)
1/4

Da Gleitkommawerte ungenau sein können, kann es zu "seltsamen" Brüchen kommen. Begrenzen Sie den Nenner, um den Bruch etwas zu vereinfachen, mit Fraction.limit_denominator():

>>> Fraction(0.185)
Fraction(3332663724254167, 18014398509481984)
>>> Fraction(0.185).limit_denominator()
Fraction(37, 200)

Wenn Sie Python 2.6 noch verwenden, wird Fraction()die floatdirekte Übergabe von a noch nicht unterstützt. Sie können jedoch die beiden oben genannten Techniken kombinieren, um:

Fraction(*0.25.as_integer_ratio())

Oder Sie können einfach die Fraction.from_float()Klassenmethode verwenden :

Fraction.from_float(0.25)

Dies macht im Wesentlichen dasselbe, z. B. nehmen Sie das Ganzzahlverhältnis-Tupel und übergeben Sie es als zwei separate Argumente.

Und eine kleine Demo mit Ihren Beispielwerten:

>>> 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

Sowohl das fractionsModul als auch die float.as_integer_ratio()Methode sind neu in Python 2.6.

Martijn Pieters
quelle
1
Vielen Dank! Option 1 funktioniert, aber Option 2 (Bruch (0,25)) gibt mir den Fehler: "TypeError: 'float'-Objekt kann nicht als Index interpretiert werden". Kannst du mir sagen warum?
user2370460
1
@ user2370460: Welche Python-Version verwenden Sie?
Martijn Pieters
5
@ user2370460: Ah, ja, nur Version 2.7 und höher unterstützen die Übergabe eines Gleitkomma- oder Dezimaltyps. Verwenden Sie Fraction(*(0.25).as_integer_ratio())stattdessen in diesem Fall.
Martijn Pieters
1
Beachten Sie, dass sowohl das fractionsModul float.as_integer_ratioals auch in Python 2.6 hinzugefügt wurden. Wenn Sie eine ältere Version von Python ausführen, sind diese nicht verfügbar.
IceArdor
3
@ user2370460: Ich habe es total geschafft, die verfügbare Fraction.from_float()Klassenmethode zu verpassen , viel klarer. Fraction.from_float(0.25).
Martijn Pieters
9
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
ajknzhol
quelle
Danke, aber das gibt mir: "TypeError: 'float' Objekt kann nicht als Index interpretiert werden". Kannst du mir sagen warum?
user2370460
1
Guter Vorschlag, aber ich möchte hinzufügen: Der Konstruktor mit einem Argument funktioniert gut für einige Zahlen, aber nicht für andere. Zum Beispiel Fraction(.2)wird Fraction(3602879701896397, 18014398509481984). Es könnte besser sein, den Konstruktor mit zwei Argumenten zu verwenden, z. Fraction(2,10)
Kevin
3
@ Kevin: Das liegt daran, dass .2 nicht genau durch einen Float dargestellt werden kann. Siehe meine Antwort für eine Problemumgehung; Begrenzen Sie den Nenner und Sie können ihn im Handumdrehen auf 2/10 zurückbringen.
Martijn Pieters
9

Martijn Pieters ausgezeichnete Antwort mit einer zusätzlichen Option aufgrund der Ungenauigkeit, die komplexeren Schwimmern innewohnt, zu erweitern. Zum Beispiel:

>>> 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

Das gewünschte mathematische Ergebnis war 8857097/10000000 das, das erreicht werden kann, indem eine Saite gegossen und dann manipuliert wird.

Bearbeitete Antwort

Ich habe einen viel einfacheren Weg gefunden, um das Genauigkeitsproblem zu lösen.

>>> Fraction(str(f))
Fraction(8857097, 10000000)

Das Umwandeln einer Zeichenfolge ermöglicht auch genaue Dezimalinstanzen

>>> Decimal(f).as_integer_ratio()
(1994440937439217, 2251799813685248)
>>> Decimal(str(f)).as_integer_ratio()
(8857097, 10000000)

Ursprüngliche Antwort

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

Jetzt testen wir es:

>>> float_to_ratio(f)
(8857097, 10000000)      # mathematically correct

Ich werde bemerken, dass diese Art der Bruchgenauigkeit nicht optimiert ist und normalerweise nicht benötigt wird, aber der Vollständigkeit halber ist sie hier. Diese Funktion vereinfacht den Bruch nicht, aber Sie können zusätzliche Verarbeitung durchführen, um ihn zu reduzieren:

>>> n = 0.5
>>> float_to_ratio(n)
(5, 10)
>>> Fraction(*float_to_ratio(n))
Fraction(1, 2)
Matt Eding
quelle
7

Wenn Sie einen richtigen Bruch drucken möchten , sollte dieses kleine Rezept Folgendes tun:

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}")

Dies wird wie folgt gedruckt:

>>> dec_to_proper_frac(3.75)
>>> "3 3/4"
Der Aelfinn
quelle