512 Stimmen

Was ist der beste Weg, um in Python Floats auf fast Gleichheit zu vergleichen?

Es ist bekannt, dass Vergleichen von Gleitkommazahlen auf Gleichheit aufgrund von Rundungs- und Präzisionsproblemen etwas kompliziert ist.

Zum Beispiel: Vergleichen von Gleitkommazahlen, Ausgabe 2012

Was ist der empfohlene Weg, um damit in Python umzugehen?

Gibt es irgendwo eine Standardbibliotheksfunktion dafür?

0 Stimmen

@tolomea: Da es von Ihrer Anwendung, Ihren Daten und Ihrem Problem abhängt - und es sich nur um eine Zeile Code handelt -, warum sollte es eine "Standardbibliotheksfunktion" geben?

14 Stimmen

@S.Lott: all, any, max, min sind im Grunde genommen Einzeiler, und sie werden nicht nur in einer Bibliothek bereitgestellt, sondern sind eingebaute Funktionen. Die Gründe des BDFL sind also nicht das. Die eine Zeile Code, die die meisten Menschen schreiben, ist ziemlich einfallslos und funktioniert oft nicht, was ein starkes Argument dafür ist, etwas Besseres bereitzustellen. Natürlich müsste ein Modul, das andere Strategien bietet, auch Warnhinweise bereitstellen, wann sie angemessen sind und vor allem wann nicht. Numerische Analyse ist schwierig, es ist kein großes Unglück, dass Sprachdesigner normalerweise keine Tools zur Unterstützung dabei entwickeln.

0 Stimmen

@Steve Jessop. Diese sammlungsbasierten Funktionen haben nicht die Anwendungs-, Daten- und Problemdomänenabhängigkeiten wie Gleitkommazahlen. Daher ist der "Einzeiler" offensichtlich nicht so wichtig wie die eigentlichen Gründe. Die numerische Analyse ist schwierig und kann kein integraler Bestandteil einer Library einer allgemeinen Zwecksprache sein.

503voto

Mark Ransom Punkte 283960

Python 3.5 fügt die math.isclose und cmath.isclose Funktionen hinzu, wie in PEP 485 beschrieben.

Wenn Sie eine frühere Version von Python verwenden, finden Sie die entsprechende Funktion in der Dokumentation.

def isclose(a, b, rel_tol=1e-09, abs_tol=0.0):
    return abs(a-b) <= max(rel_tol * max(abs(a), abs(b)), abs_tol)

rel_tol ist eine relative Toleranz, die mit der Größeren der beiden Argumente multipliziert wird; wenn die Werte größer werden, nimmt auch der erlaubte Unterschied zwischen ihnen zu, während sie immer noch als gleich betrachtet werden.

abs_tol ist eine absolute Toleranz, die in jedem Fall angewendet wird. Wenn der Unterschied geringer ist als eine der beiden Toleranzen, gelten die Werte als gleich.

51 Stimmen

Bitte beachten Sie, dass wenn a oder b ein numpy array ist, numpy.isclose funktioniert.

6 Stimmen

@marsh rel_tol ist eine relative Toleranz, sie wird mit dem größeren der Beträge der beiden Argumente multipliziert; je größer die Werte werden, desto größer wird der erlaubte Unterschied zwischen ihnen, während sie immer noch als gleich betrachtet werden. abs_tol ist eine absolute Toleranz, die in allen Fällen unverändert angewendet wird. Wenn der Unterschied kleiner ist als eine der beiden Toleranzen, werden die Werte als gleich betrachtet.

5 Stimmen

Entschuldigung, dass ich einen alten Thread wiederbelebe, aber es schien erwähnenswert zu sein, dass isclose immer dem weniger konservativen Kriterium entspricht. Ich erwähne es nur, weil dieses Verhalten für mich gegenintuitiv ist. Wenn ich zwei Kriterien angeben würde, würde ich immer erwarten, dass die kleinere Toleranz die größere tolerieren würde.

109voto

Andrew White Punkte 51732

Etwas so Einfaches wie das Folgende könnte gut genug sein:

return abs(f1 - f2) <= allowed_error

13 Stimmen

Wie der von mir bereitgestellte Link zeigt, funktioniert die Subtraktion nur, wenn Sie im Voraus die ungefähre Größenordnung der Zahlen kennen.

1 Stimmen

Dies geschieht lange nach dem Fakt, aber hier ist ein alternativer Ausdruck von @AndrewWhite's Antwort: return round(f1 - f2, some_precision_defaulting_to_seven_decimals) == 0.

16 Stimmen

In meiner Erfahrung ist die beste Methode zum Vergleichen von Gleitkommazahlen: abs(f1-f2) < tol*max(abs(f1),abs(f2)). Diese Art von relativer Toleranz ist der einzige sinnvolle Weg, um Gleitkommazahlen im Allgemeinen zu vergleichen, da sie normalerweise von Rundungsfehlern in den kleinen Dezimalstellen betroffen sind.

74voto

J.Makela Punkte 1004

Ich würde zustimmen, dass Gareth's Antwort wahrscheinlich am angemessensten als eine leichte Funktion/Lösung ist.

Aber ich dachte, es wäre hilfreich anzumerken, dass es eine verpackte Funktion dafür gibt, wenn Sie NumPy verwenden oder darüber nachdenken.

numpy.isclose(a, b, rtol=1e-05, atol=1e-08, equal_nan=False)

Ein kleiner Hinweis jedoch: Die Installation von NumPy kann je nach Plattform eine nicht ganz einfache Erfahrung sein.

3 Stimmen

"Die Installation von numpy kann je nach Plattform eine nicht-triviale Erfahrung sein."...äh Was? Auf welchen Plattformen ist es "nicht trivial", numpy zu installieren? Was genau hat es nicht trivial gemacht?

10 Stimmen

@John: Schwierig, ein 64-Bit-Binary für Windows zu erhalten. Schwierig, numpy über pip auf Windows zu bekommen.

0 Stimmen

@Ternak: Ja, mache ich, aber einige meiner Schüler benutzen Windows, also muss ich mich mit diesem Zeug auseinandersetzen.

19voto

jathanism Punkte 31729

Verwenden Sie das decimal Modul von Python, das die Klasse Decimal bereitstellt.

Aus den Kommentaren:

Es ist erwähnenswert, dass wenn Sie viele mathematische Berechnungen durchführen und Sie die Präzision von Decimal nicht unbedingt benötigen, dies die Dinge wirklich langsamer machen kann. Floats sind viel, viel schneller zu handhaben, aber ungenau. Dezimalzahlen sind äußerst genau, aber langsam.

15voto

user2745509 Punkte 341

math.isclose() wurde in Python 3.5 für das hinzugefügt (hinzugefügt). Hier ist eine Portierung davon nach Python 2. Der Unterschied zum einzeiligen Code von Mark Ransom besteht darin, dass er "inf" und "-inf" ordnungsgemäß verarbeiten kann.

def isclose(a, b, rel_tol=1e-09, abs_tol=0.0):
    '''
    Python 2 Implementierung von Python 3.5 math.isclose()
    https://github.com/python/cpython/blob/v3.5.10/Modules/mathmodule.c#L1993
    '''
    # Überprüfung der Eingaben
    if rel_tol < 0 or abs_tol < 0:
        raise ValueError("Toleranzen müssen nicht-negativ sein")

    # Kurzschluss für exakte Gleichheit -- zum Erfassen von zwei Unendlichkeiten
    # mit gleichem Vorzeichen. Und vielleicht beschleunigt es manchmal etwas.
    if a == b:
        return True

    # Hier wird der Fall von zwei Unendlichkeiten mit entgegengesetztem Vorzeichen
    # oder einer Unendlichkeit und einer endlichen Zahl erfasst. Zwei Unendlichkeiten
    # mit gleichem Vorzeichen werden durch den obigen Gleichheitscheck erfasst.
    if math.isinf(a) or math.isinf(b):
        return False

    # reguläre Berechnung durchführen
    # dies entspricht im Wesentlichen dem "schwachen" Test aus der Boost-Bibliothek
    diff = math.fabs(b - a)
    result = (((diff <= math.fabs(rel_tol * b)) or
               (diff <= math.fabs(rel_tol * a))) or
              (diff <= abs_tol))
    return result

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X