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.

15voto

Eric Postpischil Punkte 168166

Die verbreitete Ansicht, dass Gleitkommazahlen nicht auf Gleichheit verglichen werden können, ist ungenau. Gleitkommazahlen sind nicht anders als Ganzzahlen: Wenn Sie "a == b" auswerten, erhalten Sie true, wenn es sich um identische Zahlen handelt, und false, wenn nicht (unter der Voraussetzung, dass zwei NaNs natürlich nicht als identische Zahlen betrachtet werden).

Das eigentliche Problem ist folgendes: Wenn ich einige Berechnungen durchgeführt habe und mir nicht sicher bin, ob die beiden Zahlen, die ich vergleichen muss, genau korrekt sind, was dann? Dieses Problem ist sowohl für Gleitkommazahlen als auch für Ganzzahlen dasselbe. Wenn Sie den ganzzahligen Ausdruck "7/3*3" auswerten, wird er nicht gleich "7*3/3" sein.

Angenommen, wir fragen: "Wie vergleiche ich Ganzzahlen auf Gleichheit?" in einer solchen Situation. Es gibt keine einheitliche Antwort; was Sie tun sollten, hängt von der konkreten Situation ab, insbesondere von den Fehlern, die Sie haben, und dem, was Sie erreichen möchten.

Hier sind einige mögliche Optionen.

Wenn Sie ein "true"-Ergebnis erhalten möchten, wenn die mathematisch genauen Zahlen gleich wären, könnten Sie versuchen, die Eigenschaften der von Ihnen durchgeführten Berechnungen zu nutzen, um nachzuweisen, dass Sie die gleichen Fehler in den beiden Zahlen erhalten. Wenn dies möglich ist und Sie zwei Zahlen vergleichen, die sich aus Ausdrücken ergeben, die gleiche Zahlen ergeben würden, wenn sie genau berechnet werden, erhalten Sie durch den Vergleich "true". Ein anderer Ansatz besteht darin, dass Sie die Eigenschaften der Berechnungen analysieren und nachweisen könnten, dass der Fehler niemals einen bestimmten Betrag überschreitet, vielleicht 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, könnten Sie raten und auf das Beste hoffen. Eine Möglichkeit zu raten besteht darin, viele zufällige Proben auszuwerten und zu sehen, welche Art von Verteilung Sie in den Ergebnissen erhalten.

Natürlich haben wir mit der Anforderung, dass Sie "true" erhalten, wenn die mathematisch genauen Ergebnisse gleich sind, die Möglichkeit offen gelassen, dass Sie "true" erhalten, selbst wenn sie ungleich sind. (Tatsächlich können wir die Anforderung erfüllen, indem wir immer "true" zurückgeben. Dies macht die Berechnung einfach, ist jedoch im Allgemeinen unerwünscht, daher werde ich unten weitere Verbesserungen diskutieren.)

Wenn Sie ein "false"-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 für praktische Zwecke in vielen üblichen Situationen unmöglich sein. Überlegen wir uns also eine Alternative.

Eine nützliche Anforderung könnte sein, dass wir ein "false"-Ergebnis erhalten, wenn die mathematisch genauen Zahlen um mehr als einen bestimmten Betrag voneinander abweichen. Beispielsweise möchten wir berechnen, wo ein Ball, der in einem Computerspiel geworfen wird, hingeflogen ist, und wir möchten wissen, ob er einen Schläger getroffen hat. In diesem Fall möchten wir sicherlich "true" erhalten, wenn der Ball den Schläger trifft, und wir möchten "false" erhalten, wenn der Ball weit vom Schläger entfernt ist, und wir können eine falsche "true"-Antwort akzeptieren, wenn der Ball in einer mathematisch exakten Simulation den Schläger verfehlt, aber einen Millimeter davon entfernt ist, den Schläger zu treffen. In diesem Fall müssen wir nachweisen (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 (für alle interessanten Positionen) haben. Dies würde es uns ermöglichen, immer "false" zurückzugeben, wenn der Ball und der Schläger mehr als einen Millimeter voneinander entfernt sind, "true" zurückzugeben, wenn sie sich berühren, und "true" zurückzugeben, wenn sie nah genug beieinander sind, um akzeptabel zu sein.

Wie also entscheiden Sie, was Sie zurückgeben, wenn Sie Gleitkommazahlen vergleichen, hängt sehr stark von Ihrer spezifischen Situation ab.

Was die Feststellung von Fehlergrenzen für Berechnungen betrifft, so kann dies ein kompliziertes Thema sein. Jede Gleitkommazahl-Implementierung, die den IEEE-754-Standard im Rundungsmodus zum nächsten Wert verwendet, gibt die Gleitkommazahl zurück, die dem exakten Ergebnis am nächsten ist, für jede Grundoperation (insbesondere Multiplikation, Division, Addition, Subtraktion, Quadratwurzel). (Im Falle eines Gleichstands wird so gerundet, dass das niederwertige Bit gerade ist.) (Seien Sie besonders vorsichtig bei Quadratwurzeln und Divisionen; Ihre Sprachimplementierung könnte Methoden verwenden, die sich bei diesen nicht an IEEE 754 halten.) Aufgrund dieser Anforderung wissen wir, dass der Fehler in einem einzelnen Ergebnis höchstens 1/2 des Werts des niederwertigsten Bits beträgt. (Wenn es mehr wäre, wäre das Runden zu einer anderen Zahl gegangen, die innerhalb des 1/2 des Werts liegt.)

Von dort aus wird es deutlich komplizierter; der nächste Schritt besteht darin, eine Operation auszuführen, bei der eine der Eingaben bereits einen Fehler aufweist. Für einfache Ausdrücke können diese Fehler durch die Berechnungen verfolgt werden, um eine Begrenzung des endgültigen Fehlers zu erreichen. In der Praxis wird dies nur in einigen Situationen durchgeführt, wie zum Beispiel bei der Arbeit an einer hochwertigen Mathematik-Bibliothek. Und natürlich benötigen Sie eine genaue Kontrolle darüber, welche Operationen durchgeführt werden. Hochsprachen geben dem Compiler oft viel Freiraum, sodass Sie möglicherweise nicht wissen, in welcher Reihenfolge die Operationen ausgeführt werden.

Zu diesem Thema könnte (und wird) noch viel mehr geschrieben werden, aber ich muss hier aufhören. Zusammenfassend lässt sich sagen: Es gibt keine Bibliotheksroutine für diesen Vergleich, da es keine einzige Lösung gibt, die den meisten Anforderungen entspricht und es sich lohnt, in eine Bibliotheksroutine aufgenommen zu werden. (Wenn das Vergleichen mit einem relativen oder absoluten Fehlerintervall für Sie ausreicht, können Sie dies einfach ohne eine Bibliotheksroutine tun.)

4 Stimmen

Aus der oben geführten Diskussion mit Gareth McCaughan ergibt sich, dass ein korrekter Vergleich mit einem relativen Fehler im Wesentlichen darauf hinausläuft, dass "_abs(a-b) <= epsmax(2*-1022,abs(a),abs(b))", das ist nichts, was ich als einfach bezeichnen würde, und sicherlich nichts, was ich alleine herausgefunden hätte. Auch wie Steve Jessop darauf hinweist, ist es von ähnlicher Komplexität wie max, min, any und all, die alle eingebaute Funktionen sind. Daher scheint es eine gute Idee zu sein, einen Vergleich mit relativem Fehler im standardmäßigen Mathematik-Modul bereitzustellen._

0 Stimmen

(7/3*3 == 7*3/3) bewertet True in Python.

2 Stimmen

@xApple: Ich habe gerade Python 2.7.2 auf OS X 10.8.3 ausgeführt und eingegeben (7/3*3 == 7*3/3). Es hat False gedruckt.

14voto

Gareth McCaughan Punkte 19600

Ich kenne nichts in der Python-Standardbibliothek (oder anderswo), das Dawsons AlmostEqual2sComplement-Funktion implementiert. Wenn dies das Verhalten ist, das Sie wollen, müssen Sie es selbst implementieren. (In diesem Fall, anstatt Dawsons clevere bitweise Hacks zu verwenden, würden Sie wahrscheinlich besser konventionelle Tests der Form if abs(a-b) <= eps1*(abs(a)+abs(b)) + eps2 oder ähnliches verwenden. Um ein Verhalten wie Dawson zu erhalten, könnten Sie etwas wie if abs(a-b) <= eps*max(EPS,abs(a),abs(b)) für ein kleines festes EPS sagen; dies entspricht nicht genau Dawson, aber es ist ähnlich im Geist.

0 Stimmen

Ich verstehe nicht ganz, was du hier machst, aber es ist interessant. Was ist der Unterschied zwischen eps, eps1, eps2 und EPS?

0 Stimmen

eps1 und eps2 definieren eine relative und eine absolute Toleranz: Sie sind bereit zu erlauben, dass sich a und b um etwa eps1 mal das Größenordnung plus eps2 unterscheiden. eps ist eine einzelne Toleranz; Sie sind bereit zu erlauben, dass sich a und b um etwa eps mal das Größenordnung unterscheiden, mit der Bestimmung, dass alles von der Größe EPS oder kleiner als von der Größe EPS angenommen wird. Wenn Sie EPS als den kleinsten nicht-denormalen Wert Ihres Gleitkommatyps nehmen, ist dies sehr ähnlich Dawsons Komparator (außer einem Faktor von 2^#bits, weil Dawson die Toleranz in ulps misst).

2 Stimmen

Übrigens stimme ich S. Lott zu, dass das Richtige immer von Ihrer tatsächlichen Anwendung abhängt, weshalb es keine einzelne Standardbibliotheksfunktion für alle Ihre Gleitkomma-Vergleichsanforderungen gibt.

12voto

volodymyr Punkte 6618

Wenn Sie es im Testen/TDD-Kontext verwenden möchten, würde ich sagen, dass dies eine Standardmethode ist:

from nose.tools import assert_almost_equals

assert_almost_equals(x, y, places=7) # Der Standardwert ist 7

8voto

Rahul Sharma Punkte 87

Hinsichtlich absolutem Fehler, können Sie einfach überprüfen

if abs(a - b) <= error:
    print("Fast gleich")

Einige Informationen darüber, warum Float in Python seltsam handeln: Python 3 Tutorial 03 - if-else, logische Operatoren und häufige Anfängerfehler

Sie können auch math.isclose für relative Fehler verwenden.

5voto

CptHwK Punkte 97

Dies ist nützlich für den Fall, in dem Sie sicherstellen möchten, dass zwei Zahlen 'bis zur Genauigkeit' gleich sind, und es keine Notwendigkeit gibt, die Toleranz anzugeben:

  • Finden Sie die minimale Genauigkeit der beiden Zahlen

  • Runden Sie beide auf die minimale Genauigkeit und vergleichen Sie sie

    def isclose(a, b): astr = str(a) aprec = len(astr.split('.')[1]) if '.' in astr else 0 bstr = str(b) bprec = len(bstr.split('.')[1]) if '.' in bstr else 0 prec = min(aprec, bprec) return round(a, prec) == round(b, prec)

Wie geschrieben, funktioniert es nur für Zahlen ohne 'e' in ihrer String-Repräsentation (was bedeutet, dass 0.9999999999995e-4 < number <= 0.9999999999995e11)

Beispiel:

>>> isclose(10.0, 10.049)
True
>>> isclose(10.0, 10.05)
False

2 Stimmen

Das grenzenlose Konzept von Nähe wird Ihnen nicht gut dienen. isclose(1.0, 1.1) ergibt False und isclose(0.1, 0.000000000001) gibt True zurück.

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