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

330

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?

Gordon Wrigley
quelle
@tolomea: Da es von Ihrer Anwendung und Ihren Daten sowie Ihrer Problemdomäne abhängt - und es sich nur um eine Codezeile handelt - warum sollte es eine "Standardbibliotheksfunktion" geben?
S.Lott
9
@ S. Lott: all, any, max, minsind 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.
Steve Jessop
@ Steve Jessop. Diese sammlungsorientierten Funktionen haben nicht die Anwendungs-, Daten- und Problemdomänenabhängigkeiten, die Gleitkommazahlen haben. Der "Einzeiler" ist also eindeutig nicht so wichtig wie die wahren Gründe. Die numerische Analyse ist schwierig und kann kein erstklassiger Bestandteil einer universellen Sprachbibliothek sein.
S.Lott
6
@ S.Lott: Ich würde wahrscheinlich zustimmen, wenn die Standard-Python-Distribution nicht mehrere Module für XML-Schnittstellen enthält. Die Tatsache, dass verschiedene Anwendungen etwas anderes tun müssen, ist eindeutig kein Hindernis dafür, Module in das Basisset aufzunehmen, um dies auf die eine oder andere Weise zu tun. Sicherlich gibt es Tricks zum Vergleichen von Floats, die häufig wiederverwendet werden. Das grundlegendste ist eine bestimmte Anzahl von Ulps. Ich stimme also nur teilweise zu - das Problem ist, dass die numerische Analyse schwierig ist. Python könnte im Prinzip Tools bereitstellen, die es manchmal etwas einfacher machen. Ich denke, niemand hat sich freiwillig gemeldet.
Steve Jessop
4
Außerdem "läuft es auf eine schwer zu entwerfende Codezeile hinaus" - wenn es nach ordnungsgemäßer Ausführung immer noch ein Einzeiler ist, ist Ihr Monitor meiner Meinung nach breiter als meiner ;-). Wie auch immer, ich denke, der gesamte Bereich ist ziemlich spezialisiert, in dem Sinne, dass die meisten Programmierer (einschließlich mir) ihn sehr selten verwenden. In Kombination mit der Schwierigkeit wird es in den meisten Sprachen nicht ganz oben auf der Liste der "meistgesuchten" Kernbibliotheken stehen.
Steve Jessop

Antworten:

322

Python 3.5 fügt die Funktionen math.iscloseundcmath.isclose 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_tolist 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_tolist 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.

Mark Ransom
quelle
26
Beachten Sie, wann aoder bist ein numpy array, numpy.isclosefunktioniert.
Dbliss
6
@marsh rel_tolist 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_tolist 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.
Mark Ransom
5
Um den Wert dieser Antwort nicht zu mindern (ich denke, es ist eine gute), ist es erwähnenswert, dass in der Dokumentation auch steht: "Modulo-Fehlerprüfung usw., die Funktion gibt das Ergebnis von ..." Mit anderen Worten, die iscloseFunktion (oben) ist keine vollständige Implementierung.
Rkersh
5
Entschuldigung für die Wiederbelebung eines alten Threads, aber es schien erwähnenswert, dass iscloseimmer 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.
Mackie Messer
3
@ MackieMesser Sie haben natürlich ein Recht auf Ihre Meinung, aber dieses Verhalten hat für mich vollkommen Sinn gemacht. Nach Ihrer Definition könnte nichts jemals "nahe" Null sein, da eine relative Toleranz multipliziert mit Null immer Null ist.
Mark Ransom
71

Ist etwas so Einfaches wie das Folgende nicht gut genug?

return abs(f1 - f2) <= allowed_error
Andrew White
quelle
8
Wie der von mir bereitgestellte Link zeigt, funktioniert das Subtrahieren nur, wenn Sie die ungefähre Größe der Zahlen im Voraus kennen.
Gordon Wrigley
8
Nach meiner Erfahrung ist die beste Methode zum Vergleichen von Schwimmern : 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.
Sesquipedal
2
Fügen Sie einfach ein einfaches Beispiel hinzu, warum es möglicherweise nicht funktioniert : >>> abs(0.04 - 0.03) <= 0.01, ergibt es False. Ich benutzePython 2.7.10 [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
schatten
3
@schatten um fair zu sein, dieses Beispiel hat mehr mit maschineller Binärgenauigkeit / Formaten zu tun als mit dem jeweiligen Vergleichsalgo. Wenn Sie 0,03 in das System eingegeben haben, ist dies nicht wirklich die Nummer, die es in die CPU geschafft hat.
Andrew White
2
@ AndrewWhite zeigt dieses Beispiel, dass abs(f1 - f2) <= allowed_errores nicht wie erwartet funktioniert.
schatten
44

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.

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

Ein kleiner Haftungsausschluss: Die Installation von NumPy kann je nach Plattform eine nicht triviale Erfahrung sein.

J. Makela
quelle
1
"Die Installation von numpy kann je nach Plattform eine nicht triviale Erfahrung sein." ... ähm Was? Auf welchen Plattformen ist es "nicht trivial", numpy zu installieren? Was genau machte es nicht trivial?
John
10
@ John: Es ist schwierig, eine 64-Bit-Binärdatei für Windows zu erhalten. Unter pipWindows ist es schwer, numpy zu bekommen .
Ben Bolker
@Ternak: Ja, aber einige meiner Schüler verwenden Windows, also muss ich mich mit diesem Zeug befassen.
Ben Bolker
4
@ BenBolker Wenn Sie eine Open Data Science-Plattform mit Python installieren müssen, ist Anaconda continuum.io/downloads (Pandas, Numpy und mehr sofort verfügbar) der beste Weg
jrovegno
Die Installation von Anaconda ist trivial
Endolith
13

Verwenden Sie das Python- decimalModul, das die DecimalKlasse bereitstellt .

Aus den Kommentaren:

Es ist erwähnenswert, dass wenn Sie mathematisch schwere Arbeit leisten und die Genauigkeit von Dezimalstellen nicht unbedingt benötigen, dies die Dinge wirklich ins Stocken bringen kann. Floats sind viel, viel schneller zu handhaben, aber ungenau. Dezimalstellen sind extrem präzise, ​​aber langsam.

Nathanismus
quelle
11

Mir ist nichts in der Python-Standardbibliothek (oder anderswo) bekannt, das Dawsons AlmostEqual2sComplementFunktion implementiert . Wenn dies das gewünschte Verhalten ist, müssen Sie es selbst implementieren. (In diesem Fall sollten Sie wahrscheinlich keine konventionellen Tests der Form if abs(a-b) <= eps1*(abs(a)+abs(b)) + eps2oder ähnliches verwenden , anstatt Dawsons clevere bitweise Hacks zu verwenden . Um ein Dawson-ähnliches Verhalten zu erzielen, könnten Sie so etwas wie if abs(a-b) <= eps*max(EPS,abs(a),abs(b))für einige kleine Fehler sagen EPS; dies ist nicht genau das gleiche wie Dawson, aber es ist im Geist ähnlich.

Gareth McCaughan
quelle
Ich verfolge nicht ganz, was Sie hier tun, aber es ist interessant. Was ist der Unterschied zwischen eps, eps1, eps2 und EPS?
Gordon Wrigley
eps1und eps2definieren Sie eine relative und eine absolute Toleranz: Sie sind bereit zuzulassen aund bsich um etwa die eps1Zeit zu unterscheiden, in der sie plus sind eps2. epsist eine einzelne Toleranz; Sie sind bereit, zuzulassen aund bsich etwa zu unterscheiden, epswie groß sie sind, mit der Maßgabe, dass alles, was größer EPSoder kleiner ist, von Größe angenommen wird EPS. Wenn Sie EPSden 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).
Gareth McCaughan
2
Im Übrigen stimme ich S. Lott zu, dass das Richtige immer von Ihrer tatsächlichen Anwendung abhängt, weshalb es keine einzige Standardbibliotheksfunktion für alle Ihre Gleitkomma-Vergleichsanforderungen gibt.
Gareth McCaughan
@ gareth-mccaughan Wie bestimmt man den "kleinsten nicht denormalen Wert Ihres Gleitkommatyps" für Python?
Gordon Wrigley
Auf dieser Seite docs.python.org/tutorial/floatingpoint.html wird angegeben, dass fast alle Python-Implementierungen IEEE-754-Floats mit doppelter Genauigkeit verwenden. Auf dieser Seite en.wikipedia.org/wiki/IEEE_754-1985 heißt es, dass die normalisierten Zahlen, die Null am nächsten liegen, ± 2 * sind. * –1022.
Gordon Wrigley
11

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

Eric Postpischil
quelle
3
Aus der obigen Diskussion mit Gareth McCaughan ergibt sich ein korrekter Vergleich mit einem relativen Fehler im Wesentlichen auf "abs (ab) <= eps max (2 * -1022, abs (a), abs (b))", das würde ich nicht beschreiben so einfach und schon gar nicht etwas, das ich selbst ausgearbeitet hätte. Auch wie Steve Jessop betont, ist es von ähnlicher Komplexität wie max, min, any und all, die alle eingebaut sind. Ein relativer Fehlervergleich im Standard-Mathematikmodul erscheint daher als gute Idee.
Gordon Wrigley
(7/3 * 3 == 7 * 3/3) wertet True in Python aus.
xApple
@xApple: Ich habe gerade Python 2.7.2 unter OS X 10.8.3 ausgeführt und eingegeben (7/3*3 == 7*3/3). Es wurde gedruckt False.
Eric Postpischil
3
Sie haben wahrscheinlich vergessen zu tippen from __future__ import division. Wenn Sie dies nicht tun, gibt es keine Gleitkommazahlen und der Vergleich erfolgt zwischen zwei Ganzzahlen.
xApple
3
Dies ist eine wichtige Diskussion, aber nicht unglaublich hilfreich.
Dan Hulme
6

Wenn Sie es im Test- / TDD-Kontext verwenden möchten, würde ich sagen, dass dies ein Standardweg ist:

from nose.tools import assert_almost_equals

assert_almost_equals(x, y, places=7) #default is 7
volodymyr
quelle
5

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.

def isclose(a, b, rel_tol=1e-09, abs_tol=0.0):
    '''
    Python 2 implementation of Python 3.5 math.isclose()
    https://hg.python.org/cpython/file/tip/Modules/mathmodule.c#l1993
    '''
    # sanity check on the inputs
    if rel_tol < 0 or abs_tol < 0:
        raise ValueError("tolerances must be non-negative")

    # short circuit exact equality -- needed to catch two infinities of
    # the same sign. And perhaps speeds things up a bit sometimes.
    if a == b:
        return True

    # This catches the case of two infinities of opposite sign, or
    # one infinity and one finite number. Two infinities of opposite
    # sign would otherwise have an infinite relative tolerance.
    # Two infinities of the same sign are caught by the equality check
    # above.
    if math.isinf(a) or math.isinf(b):
        return False

    # now do the regular computation
    # this is essentially the "weak" test from the Boost library
    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
user2745509
quelle
2

Ich fand folgenden Vergleich hilfreich:

str(f1) == str(f2)
Kresimir
quelle
Es ist interessant, aber aufgrund von str (.1 + .2) == .3
Gordon Wrigley
str (.1 + .2) == str (.3) gibt True zurück
Henrikh Kantuni
Wie unterscheidet sich das von f1 == f2? Wenn beide nahe beieinander liegen, sich aber aufgrund der Genauigkeit immer noch unterscheiden, sind auch die Zeichenfolgendarstellungen ungleich.
MrMas
2
.1 + .2 == .3 gibt False zurück, während str (.1 + .2) == str (.3) True zurückgibt
Kresimir
4
Gibt in Python 3.7.2 str(.1 + .2) == str(.3)False zurück. Die oben beschriebene Methode funktioniert nur für Python 2.
Danibix
1

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.

eis
quelle
1

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:

if f1 ==0 and f2 == 0:
    return True
else:
    return abs(f1-f2) < tol*max(abs(f1),abs(f2))
IronYeti
quelle
1

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

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 nur für Zahlen ohne das 'e' in ihrer Zeichenfolgendarstellung (dh 0,9999999999995e-4 <number <= 0,9999999999995e11)

Beispiel:

>>> isclose(10.0,10.049)
True
>>> isclose(10.0,10.05)
False
CptHwK
quelle
Das unbegrenzte Konzept der Nähe wird Ihnen nicht gut dienen. isclose(1.0, 1.1)produziert Falseund isclose(0.1, 0.000000000001)kehrt zurück True.
KFSONE
1

So vergleichen Sie bis zu einer bestimmten Dezimalstelle ohne atol/rtol:

def almost_equal(a, b, decimal=6):
    return '{0:.{1}f}'.format(a, decimal) == '{0:.{1}f}'.format(b, decimal)

print(almost_equal(0.0, 0.0001, decimal=5)) # False
print(almost_equal(0.0, 0.0001, decimal=4)) # True 
Vlad
quelle
1

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.

def round_to(float_num, prec):
    return eval("'{:." + str(int(prec)) + "f}'.format(" + str(float_num) + ")")

def is_close(float_a, float_b, prec):
    if round_to(float_a, prec) == round_to(float_b, prec):
        return True
    return False

>>>a = 10.0
10.0
>>>b = 10.0001
10.0001
>>>print is_close(a, b, prec=3)
True
>>>print is_close(a, b, prec=4)
False

Aktualisieren:

Wie von @stepehjfox vorgeschlagen, besteht eine sauberere Methode zum Erstellen einer rount_to- Funktion unter Vermeidung von "eval" in der verschachtelten Formatierung :

def round_to(float_num, prec):
    return '{:.{precision}f}'.format(float_num, precision=prec)

Nach der gleichen Idee kann der Code mit den großartigen neuen F-Strings (Python 3.6+) noch einfacher werden :

def round_to(float_num, prec):
    return f'{float_num:.{prec}f}'

Wir könnten also sogar alles in einer einfachen und sauberen 'is_close'- Funktion zusammenfassen:

def is_close(a, b, prec):
    return f'{a:.{prec}f}' == f'{b:.{prec}f}'
Albert Alomar
quelle
1
Sie müssen nicht verwenden eval(), um eine parametrisierte Formatierung zu erhalten. So etwas return '{:.{precision}f'.format(float_num, precision=decimal_precision) sollte es tun
stephenjfox
1
Quelle für meinen Kommentar und weitere Beispiele: pyformat.info/#param_align
stephenjfox
1
Danke @stephenjfox Ich wusste nichts über verschachtelte Formatierung. Übrigens fehlen Ihrem Beispielcode die endenden geschweiften Klammern:return '{:.{precision}}f'.format(float_num, precision=decimal_precision)
Albert Alomar
1
Guter Fang und besonders gut gemachte Verbesserung mit den F-Saiten. Mit dem Tod von Python 2 um die Ecke wird dies vielleicht zur Norm
stephenjfox
0

In Bezug auf den absoluten Fehler können Sie nur überprüfen

if abs(a - b) <= error:
    print("Almost equal")

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

Rahul Sharma
quelle