wake-up-neo.com

Unittest (manchmal) schlägt fehl, weil Gleitkomma-Ungenauigkeit vorliegt

Ich habe eine Klasse Vector, die einen Punkt im dreidimensionalen Raum darstellt. Dieser Vektor hat eine Methode normalize(self, length = 1), die den Vektor nach unten/oben zu length == vec.normalize(length).length skaliert.

Das unittest für diese Methode manchmal schlägt wegen der Ungenauigkeit von Gleitkommazahlen fehl. Meine Frage ist, wie kann ich sicherstellen, dass dieser Test nicht fehlschlägt, wenn die Methoden korrekt implementiert sind? Ist es möglich,ohneauf einen ungefähren Wert zu testen?



Zusätzliche Information :

    def testNormalize(self):
        vec = Vector(random.random(), random.random(), random.random())
        self.assertEqual(vec.normalize(5).length, 5)

Diesesmanchmalergibt entweder AssertionError: 4.999999999999999 != 5 oder AssertionError: 5.000000000000001 != 5.

Note: Ich bin mir bewusst, dass das Gleitkomma-Problem in der Vector.length-Eigenschaft oder in Vector.normalize() sein kann.

52
Niklas R

1) Wie kann ich sicherstellen, dass der Test funktioniert?

Verwenden Sie assertAlmostEqual, assertNotAlmostEqual.

Aus der offiziellen Dokumentation :

assertAlmostEqual(first, second, places=7, msg=None, delta=None)

Testen Sie, dass der erste und der zweite Wert in etwa gleich sind, indem Sie die Differenz berechnen, auf die angegebene Anzahl von Dezimalstellen aufrunden (Standardwert 7) und mit Null vergleichen.

2) Ist es möglich, ohne einen ungefähren Wert zu testen?

Grundsätzlich Nr.

Die Gleitkomma-Ausgabe kann nicht umgangen werden. Sie müssen entweder das von vec.normalize angegebene Ergebnis "abrunden" oder ein nahezu gleiches Ergebnis akzeptieren (jeder der beiden Werte ist eine Näherung).

97
Rik Poggi

Durch die Verwendung eines Gleitkommawerts akzeptieren Sie eine kleine mögliche Ungenauigkeit. Daher sollten Ihre Tests prüfen, ob der berechnete Wert in einen akzeptablen Bereich fällt, z.

theoreticalValue - epsilon < normalizedValue < theoreticalValue + epsilon

dabei ist epsilon ein sehr kleiner Wert, den Sie aufgrund einer Ungenauigkeit der Gleitkommazahl für eine Variation als akzeptabel definieren.

Im Allgemeinen sollten Sie keine Gleichheit für Floats geltend machen. Stellen Sie stattdessen sicher, dass das Ergebnis innerhalb bestimmter Grenzen liegt, z.

self.assertTrue(abs(vec.normalize(5).length - 5) < 0.001)
1
Thomas Lötzer

Ich nehme an, eine Möglichkeit besteht darin, die Funktion auf Testfälle anzuwenden, für die alle Eingaben, die Ergebnisse aller Zwischenberechnungen und die Ausgabe durch float genau darstellbar sind.

Um zu zeigen:

In [2]: import math

In [4]: def norm(x, y):
   ...:     return math.sqrt(x*x + y*y)
   ...: 

In [6]: norm(3, 4) == 5
Out[6]: True

Nicht sicher, wie praktisch dies ist ...

0
NPE