Es ist bekannt, dass der Vergleich von Floats auf Gleichheit aufgrund von Rundungs- und Präzisionsproblemen etwas umständlich ist.
Zum Beispiel: https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
Was ist der empfohlene Weg, um in Python damit umzugehen?
Sicher gibt es dafür irgendwo eine Standardbibliotheksfunktion?
python
floating-point
Gordon Wrigley
quelle
quelle
all
,any
,max
,min
sind jeweils grundsätzlich Einzeiler, und sie sind nicht nur in einer Bibliothek zur Verfügung gestellt, sind sie eingebaute Funktionen. Die Gründe der BDFL sind das also nicht. Die eine Codezeile, die die meisten Leute schreiben, ist ziemlich unkompliziert und funktioniert oft nicht, was ein starker Grund ist, etwas Besseres bereitzustellen. Natürlich müsste jedes Modul, das andere Strategien bereitstellt, auch Vorbehalte enthalten, die beschreiben, wann sie angemessen sind, und, was noch wichtiger ist, wann nicht. Die numerische Analyse ist schwierig, es ist keine große Schande, dass Sprachdesigner normalerweise keine Tools versuchen, um dabei zu helfen.Antworten:
Python 3.5 fügt die Funktionen
math.isclose
undcmath.isclose
hinzu, wie in PEP 485 beschrieben .Wenn Sie eine frühere Version von Python verwenden, finden Sie die entsprechende Funktion in der Dokumentation .
rel_tol
ist eine relative Toleranz, sie wird mit der größeren der Größen der beiden Argumente multipliziert; Wenn die Werte größer werden, nimmt auch die zulässige Differenz zwischen ihnen zu, 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 die Differenz kleiner als eine dieser Toleranzen ist, werden die Werte als gleich angesehen.quelle
a
oderb
ist einnumpy
array
,numpy.isclose
funktioniert.rel_tol
ist eine relative Toleranz , die mit der größeren der beiden Argumente multipliziert wird. Wenn die Werte größer werden, nimmt auch die zulässige Differenz zwischen ihnen zu, 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 die Differenz kleiner als eine dieser Toleranzen ist, werden die Werte als gleich angesehen.isclose
Funktion (oben) ist keine vollständige Implementierung.isclose
immer das weniger konservative Kriterium eingehalten wird. Ich erwähne es nur, weil dieses Verhalten für mich nicht intuitiv ist. Wenn ich zwei Kriterien spezifizieren würde, würde ich immer erwarten, dass die kleinere Toleranz die größere ersetzt.Ist etwas so Einfaches wie das Folgende nicht gut genug?
quelle
abs(f1-f2) < tol*max(abs(f1),abs(f2))
. Diese Art der relativen Toleranz ist die einzig sinnvolle Möglichkeit, Floats im Allgemeinen zu vergleichen, da sie normalerweise von Rundungsfehlern mit kleinen Dezimalstellen betroffen sind.>>> abs(0.04 - 0.03) <= 0.01
, ergibt esFalse
. Ich benutzePython 2.7.10 [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
abs(f1 - f2) <= allowed_error
es nicht wie erwartet funktioniert.Ich würde zustimmen, dass Gareths Antwort als leichtgewichtige Funktion / Lösung wahrscheinlich am besten geeignet ist.
Aber ich dachte, es wäre hilfreich zu beachten, dass es eine gepackte Funktion dafür gibt, wenn Sie NumPy verwenden oder darüber nachdenken.
Ein kleiner Haftungsausschluss: Die Installation von NumPy kann je nach Plattform eine nicht triviale Erfahrung sein.
quelle
pip
Windows ist es schwer, numpy zu bekommen .Verwenden Sie das Python-
decimal
Modul, das dieDecimal
Klasse bereitstellt .Aus den Kommentaren:
quelle
Mir ist nichts in der Python-Standardbibliothek (oder anderswo) bekannt, das Dawsons
AlmostEqual2sComplement
Funktion implementiert . Wenn dies das gewünschte Verhalten ist, müssen Sie es selbst implementieren. (In diesem Fall sollten Sie wahrscheinlich keine konventionellen Tests der Formif abs(a-b) <= eps1*(abs(a)+abs(b)) + eps2
oder ähnliches verwenden , anstatt Dawsons clevere bitweise Hacks zu verwenden . Um ein Dawson-ähnliches Verhalten zu erzielen, könnten Sie so etwas wieif abs(a-b) <= eps*max(EPS,abs(a),abs(b))
für einige kleine Fehler sagenEPS
; dies ist nicht genau das gleiche wie Dawson, aber es ist im Geist ähnlich.quelle
eps1
undeps2
definieren Sie eine relative und eine absolute Toleranz: Sie sind bereit zuzulassena
undb
sich um etwa dieeps1
Zeit zu unterscheiden, in der sie plus sindeps2
.eps
ist eine einzelne Toleranz; Sie sind bereit, zuzulassena
undb
sich etwa zu unterscheiden,eps
wie groß sie sind, mit der Maßgabe, dass alles, was größerEPS
oder kleiner ist, von Größe angenommen wirdEPS
. Wenn SieEPS
den kleinsten nicht denormalen Wert Ihres Gleitkommatyps annehmen , ist dies dem Dawson-Komparator sehr ähnlich (mit Ausnahme eines Faktors von 2 ^ # Bits, da Dawson die Toleranz in Ulps misst).Die allgemeine Weisheit, dass Gleitkommazahlen nicht auf Gleichheit verglichen werden können, ist ungenau. Gleitkommazahlen unterscheiden sich nicht von ganzen Zahlen: Wenn Sie "a == b" auswerten, erhalten Sie true, wenn es sich um identische Zahlen handelt, und andernfalls false (mit dem Verständnis, dass zwei NaNs natürlich keine identischen Zahlen sind).
Das eigentliche Problem ist folgendes: Wenn ich einige Berechnungen durchgeführt habe und nicht sicher bin, ob die beiden Zahlen, die ich vergleichen muss, genau richtig sind, was dann? Dieses Problem ist für Gleitkommazahlen dasselbe wie für Ganzzahlen. Wenn Sie den ganzzahligen Ausdruck "7/3 * 3" auswerten, wird er nicht mit "7 * 3/3" verglichen.
Nehmen wir also an, wir fragten: "Wie vergleiche ich ganze Zahlen auf Gleichheit?" in solch einer Situation. Es gibt keine einzige Antwort; Was Sie tun sollten, hängt von der jeweiligen Situation ab, insbesondere davon, welche Art von Fehlern Sie haben und was Sie erreichen möchten.
Hier sind einige mögliche Optionen.
Wenn Sie ein "wahres" Ergebnis erhalten möchten, wenn die mathematisch exakten Zahlen gleich wären, können Sie versuchen, die Eigenschaften der von Ihnen durchgeführten Berechnungen zu verwenden, um zu beweisen, dass Sie die gleichen Fehler in den beiden Zahlen erhalten. Wenn dies machbar ist und Sie zwei Zahlen vergleichen, die sich aus Ausdrücken ergeben, die bei genauer Berechnung gleiche Zahlen ergeben würden, erhalten Sie aus dem Vergleich "wahr". Ein anderer Ansatz besteht darin, dass Sie die Eigenschaften der Berechnungen analysieren und nachweisen können, dass der Fehler niemals einen bestimmten Betrag überschreitet, möglicherweise einen absoluten Betrag oder einen Betrag relativ zu einer der Eingaben oder einer der Ausgaben. In diesem Fall können Sie fragen, ob sich die beiden berechneten Zahlen um höchstens diesen Betrag unterscheiden, und "true" zurückgeben, wenn sie innerhalb des Intervalls liegen. Wenn Sie keine Fehlergrenze nachweisen können, Sie könnten raten und auf das Beste hoffen. Eine Möglichkeit zu raten besteht darin, viele Zufallsstichproben auszuwerten und festzustellen, welche Art von Verteilung Sie in den Ergebnissen erhalten.
Da wir nur festlegen, dass Sie "wahr" werden, wenn die mathematisch genauen Ergebnisse gleich sind, haben wir natürlich die Möglichkeit offen gelassen, dass Sie "wahr" werden, auch wenn sie ungleich sind. (Tatsächlich können wir die Anforderung erfüllen, indem wir immer "true" zurückgeben. Dies vereinfacht die Berechnung, ist jedoch im Allgemeinen unerwünscht. Daher werde ich die Verbesserung der folgenden Situation erörtern.)
Wenn Sie ein "falsches" Ergebnis erhalten möchten, wenn die mathematisch genauen Zahlen ungleich wären, müssen Sie nachweisen, dass Ihre Bewertung der Zahlen unterschiedliche Zahlen ergibt, wenn die mathematisch genauen Zahlen ungleich wären. Dies kann aus praktischen Gründen in vielen häufigen Situationen unmöglich sein. Betrachten wir also eine Alternative.
Eine nützliche Anforderung könnte sein, dass wir ein "falsches" Ergebnis erhalten, wenn sich die mathematisch genauen Zahlen um mehr als einen bestimmten Betrag unterscheiden. Zum Beispiel werden wir vielleicht berechnen, wohin ein in einem Computerspiel geworfener Ball gereist ist, und wir wollen wissen, ob er einen Schläger getroffen hat. In diesem Fall möchten wir auf jeden Fall "wahr" werden, wenn der Ball auf den Schläger trifft, und wir möchten "falsch" werden, wenn der Ball weit vom Schläger entfernt ist, und wir können eine falsche "wahre" Antwort akzeptieren, wenn der Ball eintritt Eine mathematisch exakte Simulation hat den Schläger verfehlt, ist aber nur einen Millimeter vom Schlagen des Schlägers entfernt. In diesem Fall müssen wir beweisen (oder raten / schätzen), dass unsere Berechnung der Position des Balls und der Position des Schlägers einen kombinierten Fehler von höchstens einem Millimeter aufweist (für alle interessierenden Positionen). Dies würde es uns ermöglichen, immer zurückzukehren "
Wie Sie beim Vergleich von Gleitkommazahlen entscheiden, was zurückgegeben werden soll, hängt also stark von Ihrer spezifischen Situation ab.
Dies kann ein kompliziertes Thema sein, wenn es darum geht, Fehlergrenzen für Berechnungen nachzuweisen. Jede Gleitkommaimplementierung unter Verwendung des IEEE 754-Standards im Rund-zu-Nächsten-Modus gibt die Gleitkommazahl zurück, die dem exakten Ergebnis für jede Grundoperation (insbesondere Multiplikation, Division, Addition, Subtraktion, Quadratwurzel) am nächsten kommt. (Im Falle eines Gleichstands rund, damit das niedrige Bit gerade ist.) (Seien Sie besonders vorsichtig bei Quadratwurzel und Division; Ihre Sprachimplementierung verwendet möglicherweise Methoden, die für diese nicht IEEE 754 entsprechen.) Aufgrund dieser Anforderung kennen wir die Der Fehler in einem einzelnen Ergebnis beträgt höchstens die Hälfte des Wertes des niedrigstwertigen Bits. (Wenn es mehr wäre, wäre die Rundung auf eine andere Zahl gegangen, die innerhalb der Hälfte des Wertes liegt.)
Von dort aus wird es wesentlich komplizierter. Der nächste Schritt ist die Ausführung einer Operation, bei der einer der Eingänge bereits einen Fehler aufweist. Bei einfachen Ausdrücken können diese Fehler durch die Berechnungen verfolgt werden, um eine Grenze für den endgültigen Fehler zu erreichen. In der Praxis geschieht dies nur in wenigen Situationen, beispielsweise bei der Arbeit an einer hochwertigen Mathematikbibliothek. Und natürlich müssen Sie genau steuern, welche Vorgänge genau ausgeführt werden. Hochsprachen geben dem Compiler häufig viel Spielraum, sodass Sie möglicherweise nicht wissen, in welcher Reihenfolge Operationen ausgeführt werden.
Es könnte (und wird) noch viel mehr über dieses Thema geschrieben werden, aber ich muss hier aufhören. Zusammenfassend lautet die Antwort: Für diesen Vergleich gibt es keine Bibliotheksroutine, da es keine einzige Lösung gibt, die den meisten Anforderungen entspricht und die es wert ist, in eine Bibliotheksroutine aufgenommen zu werden. (Wenn der Vergleich mit einem relativen oder absoluten Fehlerintervall für Sie ausreicht, können Sie dies einfach ohne Bibliotheksroutine tun.)
quelle
(7/3*3 == 7*3/3)
. Es wurde gedrucktFalse
.from __future__ import division
. Wenn Sie dies nicht tun, gibt es keine Gleitkommazahlen und der Vergleich erfolgt zwischen zwei Ganzzahlen.Wenn Sie es im Test- / TDD-Kontext verwenden möchten, würde ich sagen, dass dies ein Standardweg ist:
quelle
math.isclose () wurde hinzugefügt , um Python 3.5 für diese ( Quellcode ). Hier ist eine Portierung davon zu Python 2. Der Unterschied zum Einzeiler von Mark Ransom besteht darin, dass es "inf" und "-inf" richtig verarbeiten kann.
quelle
Ich fand folgenden Vergleich hilfreich:
quelle
str(.1 + .2) == str(.3)
False zurück. Die oben beschriebene Methode funktioniert nur für Python 2.In einigen Fällen, in denen Sie die Darstellung der Quellennummer beeinflussen können, können Sie sie mithilfe von Ganzzahlzählern und Nennern als Brüche anstelle von Gleitkommazahlen darstellen. Auf diese Weise können Sie genaue Vergleiche anstellen.
Siehe Fraktion aus den Fraktionen für Details Modul.
quelle
Ich mochte den Vorschlag von @Sesquipedal, aber mit Modifikation (ein spezieller Anwendungsfall, wenn beide Werte 0 sind, gibt False zurück). In meinem Fall war ich auf Python 2.7 und habe nur eine einfache Funktion verwendet:
quelle
Nützlich für den Fall, dass Sie sicherstellen möchten, dass 2 Zahlen bis zur Genauigkeit gleich sind, ohne dass die Toleranz angegeben werden muss:
Finden Sie die minimale Genauigkeit der 2 Zahlen
Runden Sie beide mit minimaler Genauigkeit ab und vergleichen Sie sie
Wie geschrieben, funktioniert nur für Zahlen ohne das 'e' in ihrer Zeichenfolgendarstellung (dh 0,9999999999995e-4 <number <= 0,9999999999995e11)
Beispiel:
quelle
isclose(1.0, 1.1)
produziertFalse
undisclose(0.1, 0.000000000001)
kehrt zurückTrue
.So vergleichen Sie bis zu einer bestimmten Dezimalstelle ohne
atol/rtol
:quelle
Dies ist vielleicht ein etwas hässlicher Hack, aber es funktioniert ziemlich gut, wenn Sie nicht mehr als die Standard-Float-Genauigkeit (ca. 11 Dezimalstellen) benötigen.
Die round_to Funktion verwendet die Format - Methode aus der str eingebauter Klasse den Schwimmer in eine Zeichenfolge runden , die den Schwimmer mit der Anzahl der Dezimalstellen benötigt darstellt und wendet dann die eval eingebaute Funktion zum abgerundeten Schwimmer String zurück auf den numerischen Typ float.
Die Funktion is_close wendet nur eine einfache Bedingung auf den aufgerundeten Float an.
Aktualisieren:
Wie von @stepehjfox vorgeschlagen, besteht eine sauberere Methode zum Erstellen einer rount_to- Funktion unter Vermeidung von "eval" in der verschachtelten Formatierung :
Nach der gleichen Idee kann der Code mit den großartigen neuen F-Strings (Python 3.6+) noch einfacher werden :
Wir könnten also sogar alles in einer einfachen und sauberen 'is_close'- Funktion zusammenfassen:
quelle
eval()
, um eine parametrisierte Formatierung zu erhalten. So etwasreturn '{:.{precision}f'.format(float_num, precision=decimal_precision)
sollte es tunreturn '{:.{precision}}f'.format(float_num, precision=decimal_precision)
In Bezug auf den absoluten Fehler können Sie nur überprüfen
Einige Informationen darüber, warum Float in Python seltsam wirkt https://youtu.be/v4HhvoNLILk?t=1129
Sie können math.isclose auch für relative Fehler verwenden
quelle