Wie kann ich Doppelwerte in einem Komponententest richtig vergleichen?

20

Ich habe vor kurzem ein Zeitreihenmodul entworfen, in dem meine Zeitreihe im Wesentlichen a ist SortedDictionnary<DateTime, double>.

Jetzt möchte ich Unit-Tests erstellen, um sicherzustellen, dass dieses Modul immer funktioniert und das erwartete Ergebnis liefert.

Eine übliche Operation besteht darin, die Leistung zwischen den Punkten in der Zeitreihe zu berechnen.

Ich erstelle also eine Zeitreihe mit beispielsweise {1.0, 2.0, 4.0} (zu bestimmten Daten) und erwarte ein Ergebnis von {100%, 100%}.

Die Sache ist, wenn ich manuell eine Zeitreihe mit den Werten {1.0, 1.0} erstelle und auf Gleichheit prüfe (indem ich jeden Punkt vergleiche), würde der Test nicht bestanden, da es immer Ungenauigkeiten geben wird, wenn ich mit binären Darstellungen von Real arbeite zahlen.

Daher habe ich beschlossen, die folgende Funktion zu erstellen:

private static bool isCloseEnough(double expected, double actual, double tolerance=0.002)
{
    return squaredDifference(expected, actual) < Math.Pow(tolerance,2);
}

Gibt es eine andere übliche Art, mit einem solchen Fall umzugehen?

SRKX
quelle

Antworten:

10

Ich kann mir zwei andere Möglichkeiten vorstellen, um mit diesem Problem umzugehen:

Sie können verwenden Is.InRange:

Assert.That(result, Is.InRange(expected-tolerance, expected+tolerance));

Sie können verwenden Math.Round:

Assert.That(Math.Round(result, sigDigits), Is.EqualTo(expected));

Ich denke, dass beide Wege aussagekräftiger sind als eine dedizierte Funktion, da der Leser genau sehen kann, was mit Ihrer Nummer los ist, bevor sie mit dem erwarteten Wert verglichen wird.

dasblinkenlight
quelle
2
Nur eine Anmerkung, dass diese Antwort NUnit-spezifisch ist und das Assertionsmodell "Constraing-basiert" zeigt. Das klassische Assertionsmodell würde folgendermaßen aussehen: Assert.AreEqual (erwartet, tatsächlich, Toleranz);
RichardM
1
@RichardM: Poste das als Antwort und ich werde es auswählen, um es zu akzeptieren.
SRKX
Die Antwort von @dasblinkenlight ist richtig und fügt nur einige Details hinzu (da dies möglicherweise nicht klar ist - das klassische Behauptungsmodell ist auch NUnit). Andere Test-Frameworks (nicht MSTest) haben wahrscheinlich ein eigenes Assert-Modell, um mit Gleitkommawerten umzugehen.
RichardM
1
Assert.That(result, Is.InRange(expected-tolerance, expected+tolerance));wird scheitern, wenn tolerance/abs(expected) < 1E-16.
quant_dev
@quant_dev Du hast absolut recht. Da es in OP darum geht, Renditen als Prozentsätze zu berechnen, bin ich davon ausgegangen, dass abs(expected)dies ein einstelliger bis zweistelliger Wert ist. Ich habe auch die Toleranz in der Nähe von 1E-9 angenommen. Unter diesen Voraussetzungen könnte Ihnen dieser zugegebenermaßen vereinfachte Ansatz einigermaßen gute Dienste leisten (ich verwende ihn Is.InRangein meinen Tests).
dasblinkenlight
3

Es kommt darauf an, was Sie mit den Zahlen machen. Wenn Sie eine Methode testen, die z. B. anhand einiger Kriterien einen geeigneten Wert aus einem Eingabesatz auswählen soll, sollten Sie auf strikte Gleichheit prüfen. Wenn Sie Gleitkommaberechnungen durchführen, müssen Sie in der Regel mit einer Toleranz ungleich Null testen. Wie groß die Toleranz ist, hängt von den Berechnungen ab. Bei doppelter Genauigkeit ist es jedoch ein guter Ausgangspunkt, 1E-14 relative Toleranz für einfache Berechnungen und 1E-8 (Toleranz) für kompliziertere zu wählen . YMMV natürlich, und Sie müssen eine kleine absolute Toleranz hinzufügen, wenn das erwartete Ergebnis 0 ist.

quant_dev
quelle