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.

2voto

eis Punkte 48781

Für einige der Fälle, in denen Sie die Darstellung von Zahlen beeinflussen können, können Sie sie als Brüche anstelle von Fließkommazahlen darstellen, indem Sie einen ganzzahligen Zähler und Nenner verwenden. Auf diese Weise können Sie genaue Vergleiche durchführen.

Weitere Informationen finden Sie unter Bruch im fractions-Modul.

2voto

IronYeti Punkte 63

Ich mochte Sesquipedals Vorschlag, aber mit Modifikation (ein spezieller Anwendungsfall, bei dem beide Werte 0 zurückgeben). In meinem Fall war ich auf Python 2.7 und habe einfach eine einfache Funktion verwendet:

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

2voto

Ethan Chen Punkte 550

Wenn Sie dies in einem Test- oder TDD-Kontext mit dem pytest-Paket tun möchten, ist hier, wie:

import pytest

PRECISION = 1e-3

def assert_almost_equal():
    obtained_value = 99.99
    expected_value = 100.00
    assert obtained_value == pytest.approx(expected_value, PRECISION)

0voto

Kresimir Punkte 2712

Ich fand den folgenden Vergleich hilfreich:

str(f1) == str(f2)

0 Stimmen

Es ist interessant, aber nicht sehr praktisch, wegen str(.1 + .2) == .3

0 Stimmen

Str(.1 + .2) == str(.3) gibt True zurück

1 Stimmen

Wie unterscheidet sich dies von f1 == f2 - wenn sie sich beide nur aufgrund der Präzision unterscheiden, werden auch die Zeichenkettenrepräsentationen ungleich sein.

0voto

Albert Alomar Punkte 117

Dies ist vielleicht ein etwas hässlicher Hack, funktioniert aber ziemlich gut, wenn Sie nicht mehr als die Standard-Gleitkommagenauigkeit benötigen (etwa 11 Dezimalstellen).

Die round_to-Funktion verwendet die Format-Methode der integrierten str-Klasse, um die Gleitkommazahl auf eine Zeichenkette zu runden, die die Gleitkommazahl mit der erforderlichen Anzahl von Dezimalstellen darstellt, und wendet dann die integrierte Funktion eval auf die gerundete Gleitkommazahl-Zeichenkette an, um zum Gleitkommazahl-Numeriktyp zurückzukehren.

Die is_close-Funktion wendet einfach eine einfache Bedingung auf die aufgerundete Gleitkommazahl 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

Update:

Wie von @stepehjfox vorgeschlagen, ein saubererer Weg, um eine round_to-Funktion zu erstellen, die "eval" vermeidet, ist die Verwendung von verschachtelter Formatierung:

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

Dem gleichen Gedanken folgend kann der Code sogar noch einfacher sein, wenn die großartigen neuen f-Strings (Python 3.6+) verwendet werden:

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

Also könnten wir alles sogar in einer einfachen und sauberen Funktion 'is_close' zusammenfassen:

def is_close(a, b, prec):
    return f'{a:.{prec}f}' == f'{b:.{prec}f}'

1 Stimmen

Sie müssen eval() nicht verwenden, um parameterisierte Formatierung zu erhalten. Etwas wie return '{:.{precision}f'.format(float_num, precision=decimal_precision) sollte es tun

1 Stimmen

Quelle für meinen Kommentar und weitere Beispiele: pyformat.info/#param_align

1 Stimmen

Vielen Dank @stephenjfox, ich wusste nicht über die verschachtelte Formatierung Bescheid. Übrigens fehlen in Ihrem Beispielcode die abschließenden geschweiften Klammern: return '{:.{precision}}f'.format(float_num, precision=decimal_precision)

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