Following system colour scheme Selected dark colour scheme Selected light colour scheme

Python Enhancement Proposals

PEP 485 – Eine Funktion zum Testen der ungefähren Gleichheit

Autor:
Christopher Barker <PythonCHB at gmail.com>
Status:
Final
Typ:
Standards Track
Erstellt:
20. Januar 2015
Python-Version:
3.5
Post-History:

Resolution:
Python-Dev Nachricht

Inhaltsverzeichnis

Zusammenfassung

Dieser PEP schlägt die Hinzufügung einer isclose() Funktion zum Standardbibliotheksmodul math vor, die bestimmt, ob ein Wert ungefähr gleich oder „nah“ an einem anderen Wert ist.

Begründung

Gleitkommawerte enthalten eine begrenzte Präzision, die dazu führt, dass sie einige Werte nicht exakt darstellen können und Fehler bei wiederholten Berechnungen akkumulieren. Daher ist es üblicher Rat, einen Gleichheitsvergleich nur in sehr spezifischen Situationen zu verwenden. Oft passt ein Ungleichheitsvergleich, aber es gibt Zeiten (oft beim Testen), in denen der Programmierer feststellen möchte, ob ein berechneter Wert „nah“ an einem erwarteten Wert ist, ohne dass er exakt gleich sein muss. Dies ist besonders beim Testen üblich und nicht immer offensichtlich, wie es gemacht wird, so dass es eine nützliche Ergänzung zur Standardbibliothek wäre.

Bestehende Implementierungen

Die Standardbibliothek enthält die Methode unittest.TestCase.assertAlmostEqual, aber sie

  • Ist in der Klasse unittest.TestCase versteckt
  • Ist eine Assertion, sodass man sie nicht als allgemeinen Test an der Kommandozeile usw. verwenden kann (leicht).
  • Ist ein Test auf absolute Differenz. Oft erfordert das Maß der Differenz, insbesondere für Gleitkommazahlen, einen relativen Fehler, d.h. „Sind diese beiden Werte innerhalb von x% voneinander?“, anstatt eines absoluten Fehlers. Insbesondere wenn die Größenordnung der Werte a priori unbekannt ist.

Das numpy-Paket hat die Funktionen allclose() und isclose(), aber sie sind nur mit numpy verfügbar.

Die Tests des Statistikpakets enthalten eine Implementierung, die für seine Unit-Tests verwendet wird.

Man kann auch Diskussionen und Beispielimplementierungen auf Stack Overflow und anderen Hilfsseiten finden.

Viele andere Nicht-Python-Systeme bieten einen solchen Test an, darunter die Boost C++-Bibliothek und die APL-Sprache [4].

Diese bestehenden Implementierungen deuten darauf hin, dass dies ein häufiger Bedarf ist und nicht trivial selbst zu schreiben ist, was ihn zu einem Kandidaten für die Standardbibliothek macht.

Vorgeschlagene Implementierung

HINWEIS: Dieser PEP ist das Ergebnis ausführlicher Diskussionen auf der python-ideas-Liste [1].

Die neue Funktion wird in das math-Modul aufgenommen und hat die folgende Signatur

isclose(a, b, rel_tol=1e-9, abs_tol=0.0)

a und b: sind die beiden Werte, die auf relative Nähe getestet werden sollen

rel_tol: ist die relative Toleranz – sie ist der erlaubte Fehlerbetrag, relativ zum größeren Absolutwert von a oder b. Zum Beispiel, um eine Toleranz von 5% festzulegen, übergeben Sie tol=0.05. Die Standardtoleranz beträgt 1e-9, was sicherstellt, dass die beiden Werte innerhalb von etwa 9 Dezimalstellen gleich sind. rel_tol muss größer als 0.0 sein

abs_tol: ist ein minimaler absoluter Toleranzpegel – nützlich für Vergleiche in der Nähe von Null.

Modulo-Fehlerprüfung usw. gibt die Funktion das Ergebnis von zurück

abs(a-b) <= max( rel_tol * max(abs(a), abs(b)), abs_tol )

Der Name, isclose, wurde zur Konsistenz mit den bestehenden isnan und isinf gewählt.

Umgang mit nicht-endlichen Zahlen

Die speziellen IEEE 754-Werte NaN, inf und -inf werden gemäß den IEEE-Regeln behandelt. Insbesondere wird NaN nicht als gleich zu irgendeinem anderen Wert, einschließlich NaN, betrachtet. inf und -inf werden nur als gleich zu sich selbst betrachtet.

Nicht-Gleitkommatypen

Der primäre Anwendungsfall sind voraussichtlich Gleitkommazahlen. Benutzer möchten jedoch möglicherweise auch andere numerische Typen ähnlich vergleichen. Theoretisch sollte es für jeden Typ funktionieren, der abs(), Multiplikation, Vergleiche und Subtraktion unterstützt. Die Implementierung im math-Modul ist jedoch in C geschrieben und kann daher nicht (leicht) das Duck-Typing von Python verwenden. Vielmehr werden die an die Funktion übergebenen Werte vor der Berechnung in den Gleitkommatyp konvertiert. Die Übergabe von Typen (oder Werten), die nicht in Gleitkommazahlen konvertiert werden können, löst eine entsprechende Ausnahme aus (TypeError, ValueError oder OverflowError).

Der Code wird getestet, um mindestens einige Werte dieser Typen zu unterstützen

  • Decimal
  • int
  • Fraction
  • complex: Für komplexe Zahlen wird eine Begleitfunktion zum cmath-Modul hinzugefügt. In cmath.isclose() werden die Toleranzen als Gleitkommazahlen angegeben, und der Absolutwert der komplexen Werte wird für Skalierung und Vergleich verwendet. Wenn eine komplexe Toleranz übergeben wird, wird deren Absolutwert als Toleranz verwendet.

HINWEIS: Es könnte sinnvoll sein, eine Decimal.isclose() hinzuzufügen, die korrekt und vollständig mit dem Dezimaltyp funktioniert, aber das ist nicht Teil dieses PEP.

Verhalten in der Nähe von Null

Der relative Vergleich ist problematisch, wenn einer der Werte Null ist. Per Definition ist kein Wert im Verhältnis zu Null klein. Und rechnerisch, wenn einer der Werte Null ist, ist die Differenz der Absolutwert des anderen Werts, und die berechnete absolute Toleranz ist rel_tol mal diesem Wert. Wenn rel_tol kleiner als eins ist, wird die Differenz niemals kleiner als die Toleranz sein.

Obwohl mathematisch korrekt, gibt es viele Anwendungsfälle, in denen ein Benutzer wissen muss, ob ein berechneter Wert „nah“ an Null ist. Dies erfordert einen Test auf absolute Toleranz. Wenn der Benutzer diese Funktion in einer Schleife oder Comprehension aufrufen muss, bei der einige, aber nicht alle erwarteten Werte Null sein können, ist es wichtig, dass sowohl eine relative als auch eine absolute Toleranz mit einer einzigen Funktion und einem einzigen Satz von Parametern getestet werden können.

Ein ähnliches Problem tritt auf, wenn die beiden zu vergleichenden Werte Null überschreiten: Wenn a ungefähr gleich -b ist, werden a und b niemals als „nah“ berechnet.

Um diesen Fall zu behandeln, kann ein optionaler Parameter, abs_tol, verwendet werden, um eine Mindesttoleranz festzulegen, die bei sehr kleiner oder nuller berechneter relativer Toleranz verwendet wird. Das heißt, die Werte werden immer als nah betrachtet, wenn die Differenz zwischen ihnen kleiner als abs_tol ist.

Der Standardwert für die absolute Toleranz ist Null, da es keinen für den allgemeinen Fall geeigneten Wert gibt. Es ist unmöglich, einen geeigneten Wert zu kennen, ohne die wahrscheinlichen Werte für einen bestimmten Anwendungsfall zu kennen. Wenn alle getesteten Werte in der Größenordnung von eins liegen, wäre ein Wert von etwa 1e-9 angemessen, aber das wäre viel zu groß, wenn die erwarteten Werte in der Größenordnung von 1e-9 oder kleiner liegen.

Jeder nicht-null Standardwert könnte dazu führen, dass Benutzertests völlig unangemessen bestanden werden. Wenn andererseits ein Test gegen Null beim ersten Mal mit Standardwerten fehlschlägt, wird der Benutzer aufgefordert, einen für das Problem geeigneten Wert auszuwählen, um den Test bestehen zu lassen.

HINWEIS: Der Autor dieses PEP hat beschlossen, viele seiner Tests, die die numpy allclose()-Funktion verwenden, die eine Standard-Absoluttoleranz bietet, nochmals durchzugehen und sicherzustellen, dass der Standardwert angemessen ist.

Wenn der Benutzer den Parameter rel_tol auf 0.0 setzt, beeinflusst nur die absolute Toleranz das Ergebnis. Obwohl es nicht das Ziel der Funktion ist, ermöglicht es auch die Verwendung als rein absolute Toleranzprüfung.

Implementierung

Eine Beispielimplementierung in Python ist (Stand 22. Januar 2015) auf GitHub verfügbar

https://github.com/PythonCHB/close_pep/blob/master/is_close.py

Diese Implementierung hat eine Flagge, die es dem Benutzer ermöglicht, auszuwählen, welcher relative Toleranztest angewendet werden soll – dieser PEP schlägt nicht vor, diese beizubehalten, sondern den schwachen Test auszuwählen.

Es gibt auch Entwürfe dieses PEP und Testcode usw. dort

https://github.com/PythonCHB/close_pep

Relative Differenz

Es gibt im Wesentlichen zwei Möglichkeiten, darüber nachzudenken, wie nah zwei Zahlen beieinander liegen

Absoluter Unterschied: einfach abs(a-b)

Relativer Unterschied: abs(a-b)/skalierungsfaktor [2].

Der absolute Unterschied ist so trivial, dass sich dieser Vorschlag auf den relativen Unterschied konzentriert.

Normalerweise ist der Skalierungsfaktor eine Funktion der betrachteten Werte, zum Beispiel

  1. Der Absolutwert eines der Eingabewerte
  2. Der maximale Absolutwert der beiden
  3. Der minimale Absolutwert der beiden.
  4. Der Absolutwert des arithmetischen Mittels der beiden

Dies führt zu den folgenden Möglichkeiten, um festzustellen, ob zwei Werte, a und b, nahe beieinander liegen.

  1. abs(a-b) <= tol*abs(a)
  2. abs(a-b) <= tol * max( abs(a), abs(b) )
  3. abs(a-b) <= tol * min( abs(a), abs(b) )
  4. abs(a-b) <= tol * abs(a + b)/2

HINWEIS: (2) und (3) können auch geschrieben werden als

  1. (abs(a-b) <= abs(tol*a)) oder (abs(a-b) <= abs(tol*b))
  2. (abs(a-b) <= abs(tol*a)) und (abs(a-b) <= abs(tol*b))

(Boost bezieht sich auf diese als „schwache“ und „starke“ Formulierungen [3]) Diese können ein kleines bisschen recheneffizienter sein und werden daher im Beispielcode verwendet.

Jede dieser Formulierungen kann zu leicht unterschiedlichen Ergebnissen führen. Wenn jedoch der Toleranzwert klein ist, sind die Unterschiede sehr gering. Tatsächlich oft weniger als die verfügbare Gleitkommapräzision.

Wie viel Unterschied macht es?

Bei der Auswahl einer Methode zur Bestimmung der Nähe möchte man vielleicht wissen, wie viel Unterschied es machen könnte, den einen oder den anderen Test zu verwenden – d.h. wie viele Werte gibt es (oder welche Wertebereiche), die den einen Test bestehen, aber nicht den anderen.

Der größte Unterschied besteht zwischen den Optionen (2) und (3), bei denen die zulässige absolute Differenz entweder durch den größeren oder den kleineren der Werte skaliert wird.

Definieren Sie delta als die Differenz zwischen der zulässigen absoluten Toleranz, die durch den größeren Wert definiert ist, und der durch den kleineren Wert definierten. Das heißt, der Betrag, um den sich die beiden Eingabewerte unterscheiden müssen, um ein anderes Ergebnis von den beiden Tests zu erhalten. tol ist der Wert der relativen Toleranz.

Gehen Sie davon aus, dass a der größere Wert ist und dass sowohl a als auch b positiv sind, um die Analyse etwas zu vereinfachen. delta ist daher

delta = tol * (a-b)

oder

delta / tol = (a-b)

Die größte absolute Differenz, die den Test bestehen würde: (a-b), entspricht der Toleranz mal dem größeren Wert

(a-b) = tol * a

Einsetzen in den Ausdruck für delta

delta / tol = tol * a

also

delta = tol**2 * a

Zum Beispiel für a = 10, b = 9, tol = 0.1 (10%)

maximale Toleranz tol * a == 0.1 * 10 == 1.0

minimale Toleranz tol * b == 0.1 * 9.0 == 0.9

delta = (1.0 - 0.9) = 0.1 oder tol**2 * a = 0.1**2 * 10 = .1

Der absolute Unterschied zwischen den maximalen und minimalen Toleranztests könnte in diesem Fall erheblich sein. Der primäre Anwendungsfall für die vorgeschlagene Funktion ist jedoch das Testen von Berechnungsergebnissen. In diesem Fall wird wahrscheinlich eine relative Toleranz mit einer viel geringeren Größenordnung gewählt.

Zum Beispiel ist eine relative Toleranz von 1e-8 etwa die halbe Präzision, die in einem Python-Float verfügbar ist. In diesem Fall ist die Differenz zwischen den beiden Tests 1e-8**2 * a oder 1e-16 * a, was nahe am Präzisionslimit eines Python-Floats liegt. Wenn die relative Toleranz auf den vorgeschlagenen Standardwert von 1e-9 (oder kleiner) gesetzt wird, geht der Unterschied zwischen den beiden Tests durch die Präzisionsgrenzen von Gleitkommazahlen verloren. Das heißt, jede der vier Methoden liefert für alle Werte von a und b exakt dieselben Ergebnisse.

Darüber hinaus werden in der gängigen Praxis Toleranzen auf 1 signifikante Ziffer definiert – das heißt, 1e-9 spezifiziert etwa 9 Dezimalstellen Genauigkeit. Der Unterschied zwischen den verschiedenen möglichen Tests liegt also weit unterhalb der Präzision, mit der die Toleranz spezifiziert wird.

Symmetrie

Ein relativer Vergleich kann entweder symmetrisch oder unsymmetrisch sein. Für einen symmetrischen Algorithmus

isclose(a,b) ist immer gleich isclose(b,a)

Wenn ein relativer Nähe-Test nur einen der Werte verwendet (wie (1) oben), dann ist das Ergebnis unsymmetrisch, d.h. isclose(a,b) ist nicht notwendigerweise gleich isclose(b,a).

Welcher Ansatz am besten geeignet ist, hängt von der gestellten Frage ab. Wenn die Frage lautet: „Sind diese beiden Zahlen nahe beieinander?“, gibt es keine offensichtliche Ordnung, und ein symmetrischer Test ist am besten geeignet.

Wenn die Frage jedoch lautet: „Liegt der berechnete Wert innerhalb von x% dieses bekannten Werts?“, dann ist es angebracht, die Toleranz auf den bekannten Wert zu skalieren, und ein unsymmetrischer Test ist am besten geeignet.

Aus dem vorherigen Abschnitt ist klar, dass jeder Ansatz in den gängigen Anwendungsfällen dieselben oder ähnliche Ergebnisse liefern würde. In diesem Fall ist das Ziel dieses Vorschlags, eine Funktion bereitzustellen, die am wenigsten überraschende Ergebnisse liefert.

Der symmetrische Ansatz bietet eine ansprechende Konsistenz – er spiegelt die Symmetrie der Gleichheit wider und verwirrt die Leute weniger wahrscheinlich. Ein symmetrischer Test entbindet den Benutzer auch von der Notwendigkeit, über die Reihenfolge der Argumente nachzudenken. Es wurde auch darauf hingewiesen, dass es Fälle geben kann, in denen die Reihenfolge der Auswertung nicht klar definiert ist, zum Beispiel beim Vergleich einer Menge von Werten untereinander.

Es kann Fälle geben, in denen ein Benutzer wissen muss, dass ein Wert innerhalb eines bestimmten Bereichs eines bekannten Werts liegt. In diesem Fall ist es einfach genug, den Test direkt zu schreiben

if a-b <= tol*a:

(angenommen, a > b in diesem Fall). Es gibt wenig Bedarf, eine Funktion für diesen speziellen Fall bereitzustellen.

Dieser Vorschlag verwendet einen symmetrischen Test.

Welcher symmetrische Test?

Drei symmetrische Tests werden betrachtet

Der Fall, der das arithmetische Mittel der beiden Werte verwendet, erfordert, dass die Werte zuerst addiert und dann durch 2 geteilt werden, was bei sehr großen Zahlen zu zusätzlichem Überlauf zu inf führen könnte, oder dass jeder Wert vor der Addition durch zwei geteilt wird, was bei sehr kleinen Zahlen zu Unterlauf zu Null führen könnte. Dieser Effekt würde nur am äußersten Limit von Gleitkommawerten auftreten, aber es wurde entschieden, dass es keinen Nutzen für die Methode gibt, der es wert wäre, den Funktionsbereich zu reduzieren oder die Komplexität der Werteprüfung zur Bestimmung der Berechnungsreihenfolge hinzuzufügen.

Damit bleiben der Boost „schwache“ Test (2) – oder die Verwendung des größeren Werts zur Skalierung der Toleranz, oder der Boost „starke“ (3) Test, der den kleineren der Werte zur Skalierung der Toleranz verwendet. Für kleine Toleranzen liefern sie dasselbe Ergebnis, aber dieser Vorschlag verwendet den Boost „schwachen“ Testfall: Er ist symmetrisch und liefert ein nützlicheres Ergebnis für sehr große Toleranzen.

Große Toleranzen

Der häufigste Anwendungsfall sind erwartungsgemäß kleine Toleranzen – in der Größenordnung des Standardwerts 1e-9. Es kann jedoch Anwendungsfälle geben, in denen ein Benutzer wissen möchte, ob zwei ziemlich unterschiedliche Werte innerhalb eines bestimmten Bereichs voneinander liegen: „Ist a innerhalb von 200% (rel_tol = 2.0) von b? In diesem Fall würde der starke Test niemals anzeigen, dass zwei Werte innerhalb dieses Bereichs voneinander liegen, wenn einer davon Null ist. Der schwache Fall würde jedoch den größeren (nicht-null) Wert für den Test verwenden und somit True zurückgeben, wenn ein Wert Null ist. Zum Beispiel: Ist 0 innerhalb von 200% von 10? 200% von zehn sind 20, also liegt der Bereich innerhalb von 200% von zehn zwischen -10 und +30. Null liegt innerhalb dieses Bereichs, also gibt er True zurück.

Standardwerte

Standardwerte sind für die relative und absolute Toleranz erforderlich.

Standard für relative Toleranz

Die relative Toleranz, die erforderlich ist, damit zwei Werte als „nah“ betrachtet werden, ist stark vom Anwendungsfall abhängig. Dennoch muss die relative Toleranz größer als 1e-16 sein (ungefähre Präzision eines Python-Floats). Der Wert 1e-9 wurde gewählt, da er die größte relative Toleranz ist, bei der die verschiedenen möglichen Methoden dieselben Ergebnisse liefern, und er auch etwa die halbe Präzision eines Python-Floats beträgt. Im Allgemeinen wird erwartet, dass ein guter numerischer Algorithmus nicht mehr als etwa die Hälfte der verfügbaren Ziffern an Genauigkeit verliert, und wenn eine viel größere Toleranz akzeptabel ist, sollte der Benutzer in diesem Fall den richtigen Wert berücksichtigen. Daher wird erwartet, dass 1e-9 für viele Fälle „einfach funktioniert“.

Standard für absolute Toleranz

Der Wert der absoluten Toleranz wird hauptsächlich für Vergleiche mit Null verwendet. Die absolute Toleranz, die erforderlich ist, um festzustellen, ob ein Wert „nah“ an Null ist, ist stark vom Anwendungsfall abhängig. Es gibt auch im Wesentlichen keine Grenzen für den nützlichen Bereich – erwartete Werte liegen denkbar irgendwo innerhalb der Grenzen eines Python-Floats. Daher wird ein Standardwert von 0.0 gewählt.

Wenn ein Benutzer für einen bestimmten Anwendungsfall mit Null vergleichen muss, wird der Test beim ersten Mal garantiert fehlschlagen, und der Benutzer kann einen geeigneten Wert auswählen.

Es wurde vorgeschlagen, dass der Vergleich mit Null tatsächlich ein häufiger Anwendungsfall ist (Beweise deuten darauf hin, dass die numpy-Funktionen oft mit Null verwendet werden). In diesem Fall wäre es wünschenswert, einen „nützlichen“ Standardwert zu haben. Werte um 1e-8 wurden vorgeschlagen, etwa die halbe Gleitkommagenauigkeit für Werte um eins.

Zitat aus „The Zen of Python“: „Bei Mehrdeutigkeit weise den Versuch zu raten zurück.“ Zu raten, dass Benutzer sich am häufigsten mit Werten nahe 1,0 beschäftigen würden, würde zu fälschlicherweise bestandenen Tests führen, wenn sie mit kleineren Werten verwendet werden – dies ist potenziell schädlicher, als den Benutzer zu zwingen, nachdenklich einen geeigneten Wert auszuwählen.

Erwartete Verwendungen

Der primäre erwartete Anwendungsfall sind verschiedene Formen des Testens – „liegen die berechneten Ergebnisse nahe an dem, was ich als Ergebnis erwarte?“. Diese Art von Test kann Teil einer formalen Unit-Testing-Suite sein oder auch nicht. Solche Tests könnten einmalig an der Kommandozeile, in einem IPython-Notebook, Teil von Doctests oder einfache Asserts in einem if __name__ == "__main__"-Block verwendet werden.

Es wäre auch eine geeignete Funktion für die Abbruchkriterien einer einfachen iterativen Lösung einer impliziten Funktion

guess = something
while True:
    new_guess = implicit_function(guess, *args)
    if isclose(new_guess, guess):
        break
    guess = new_guess

Ungeeignete Verwendungen

Ein Anwendungsfall für Gleitkommavergleiche ist das Testen der Genauigkeit eines numerischen Algorithmus. In diesem Fall würde der numerische Analytiker jedoch idealerweise eine sorgfältige Fehlerfortpflanzungsanalyse durchführen und genau wissen, was zu testen ist. Es ist auch wahrscheinlich, dass ULP (Unit in the Last Place)-Vergleiche erforderlich sind. Obwohl diese Funktion in solchen Situationen nützlich sein mag, ist sie nicht dazu bestimmt, ohne sorgfältige Überlegung auf diese Weise verwendet zu werden.

Andere Ansätze

unittest.TestCase.assertAlmostEqual

(https://docs.pythonlang.de/3/library/unittest.html#unittest.TestCase.assertAlmostEqual)

Testet, ob Werte ungefähr (oder nicht ungefähr) gleich sind, indem die Differenz berechnet, auf die angegebene Anzahl von Dezimalstellen (Standard 7) gerundet und mit Null verglichen wird.

Diese Methode ist rein ein Test auf absolute Toleranz und adressiert nicht die Notwendigkeit eines relativen Toleranztests.

numpy isclose()

https://docs.scipy.de/doc/numpy-dev/reference/generated/numpy.isclose.html

Das numpy-Paket stellt die vektorisierten Funktionen isclose() und allclose() für ähnliche Anwendungsfälle wie dieser Vorschlag bereit

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

Gibt ein boolesches Array zurück, bei dem zwei Arrays elementweise innerhalb einer Toleranz gleich sind.

Die Toleranzwerte sind positiv, typischerweise sehr kleine Zahlen. Die relative Differenz (rtol * abs(b)) und die absolute Differenz atol werden addiert, um sie mit der absoluten Differenz zwischen a und b zu vergleichen.

Bei diesem Ansatz werden die absolute und die relative Toleranz addiert, anstatt der oder-Methode, die in diesem Vorschlag verwendet wird. Dies ist rechnerisch einfacher, und wenn die relative Toleranz größer als die absolute Toleranz ist, hat die Addition keine Auswirkung. Wenn jedoch die absolute und die relative Toleranz von ähnlicher Größenordnung sind, ist die zulässige Differenz etwa doppelt so groß wie erwartet.

Dies macht die Funktion schwerer verständlich, ohne in diesem Kontext einen rechnerischen Vorteil.

Noch kritischer ist, dass, wenn die übergebenen Werte klein im Vergleich zur absoluten Toleranz sind, die relative Toleranz vollständig überdeckt wird, vielleicht unerwartet.

Deshalb ist in diesem Vorschlag die absolute Toleranz standardmäßig Null – der Benutzer muss einen für die jeweiligen Werte geeigneten Wert wählen.

Boost Gleitkommavergleich

Das Boost-Projekt ( [3] ) bietet eine Funktion für den Gleitkommavergleich. Es ist ein symmetrischer Ansatz mit sowohl „schwachen“ (größte der beiden relativen Fehler) als auch „starken“ (kleinste der beiden relativen Fehler) Optionen. Dieser Vorschlag verwendet den Boost „schwachen“ Ansatz. Es gibt keinen Grund, die API durch die Option zur Auswahl unterschiedlicher Methoden zu verkomplizieren, wenn die Ergebnisse in den meisten Fällen ähnlich sind und der Benutzer ohnehin wahrscheinlich nicht weiß, welche er auswählen soll.

Alternative Vorschläge

Ein Rezept

Der primäre alternative Vorschlag war, überhaupt keine Standardbibliotheksfunktion bereitzustellen, sondern stattdessen ein Rezept für die Benutzer bereitzustellen. Dies hätte den Vorteil, dass das Rezept die verschiedenen Optionen bereitstellen und erklären könnte, und der Benutzer die für ihn am besten geeignete auswählen könnte. Dies würde jedoch erfordern, dass jeder, der einen solchen Test benötigt, die Funktion zumindest in seine Codebasis kopiert und die zu verwendende Vergleichsmethode auswählt.

zero_tol

Eine Möglichkeit war, einen Parameter für die Nulltoleranz anstelle des Parameters für die absolute Toleranz bereitzustellen. Dies wäre eine absolute Toleranz, die nur angewendet würde, wenn eines der Argumente exakt Null ist. Dies hätte den Vorteil, das vollständige relative Toleranzverhalten für alle nicht-null Werte beizubehalten, während Vergleiche mit Null funktionieren. Es würde jedoch auch zu dem potenziell überraschenden Ergebnis führen, dass ein kleiner Wert „nah“ an Null sein könnte, aber nicht „nah“ an einem noch kleineren Wert. Z.B. ist 1e-10 „nah“ an Null, aber nicht „nah“ an 1e-11.

Keine absolute Toleranz

Angesichts der Probleme beim Vergleich mit Null wäre eine weitere Möglichkeit gewesen, nur eine relative Toleranz bereitzustellen und den Vergleich mit Null fehlschlagen zu lassen. In diesem Fall müsste der Benutzer einen einfachen absoluten Test durchführen: abs(val) < zero_tol, wenn der Vergleich Null einschloss.

Dies würde jedoch nicht zulassen, dass derselbe Aufruf für eine Sequenz von Werten verwendet wird, z.B. in einer Schleife oder Comprehension. Dadurch wäre die Funktion weitaus weniger nützlich. Es ist anzumerken, dass der Standardwert abs_tol=0.0 denselben Effekt erzielt, wenn der Standardwert nicht überschrieben wird.

Andere Tests

Die anderen in Betracht gezogenen Tests werden alle im Abschnitt Relative Error oben besprochen.

Referenzen


Quelle: https://github.com/python/peps/blob/main/peps/pep-0485.rst

Zuletzt geändert: 2025-02-01 08:59:27 GMT