2012-12-19 11 views
17

die Aussagen GegebenFloat to Double-Konvertierung - Beste Behauptung in einem Unit-Test?

float f = 7.1f; 
double d = f; 

Was in einem Unit-Test über d können wir behaupten?


Zum Beispiel funktioniert das nicht:

Console.WriteLine(d == 7.1d); // false 
Console.WriteLine(d < 7.1d + float.Epsilon); // true by luck 
Console.WriteLine(d > 7.1d - float.Epsilon); // false (less luck) 

Der beste Weg, ich ist bisher gefunden, den Wert zu konvertieren zurück:

float f2 = (float)d; 
Console.WriteLine(f2 == f); // true 

Welches ist das gleiche wie die Brute wäre weg zu sagen

Console.WriteLine(d == 7.1f); // 7.1f implicitly converted to double as above 

Diese Frage bezieht sich NICHT auf Doppel- und Float-Präzision, sondern auf die pragmatische Frage, wie ein Unit-Test die Grenzen von d am besten beschreiben kann. In meinem Fall ist d das Ergebnis einer Umwandlung, die in einem Code auftritt, der durch eine leichte Codeerzeugung erzeugt wird. Beim Testen dieser Codegenerierung muss ich Aussagen über das Ergebnis dieser Funktion machen, und dies läuft letztlich auf die einfache Frage oben hinaus.

+0

Welches Unit Testing Framework verwenden Sie? Sie können sich nicht unbedingt gegen eine Bedingung aussprechen, die Sie z. 'Assert.IsTrue (Bedingung)', können Sie möglicherweise z. 'Assert.AreEqual (value1, value2)', das die Äquivalenz zwischen numerischen Formaten verarbeiten kann. – StuperUser

+0

Ich bin mir immer noch nicht sicher, was Sie hier testen möchten. Ein Double hat mehr Präzision als ein Float und wird daher kaum "gleich" sein. – CodeCaster

+0

Wenn Sie das letzte f in der Konstante, die für die Initialisierung des Floats verwendet wird, weglassen, könnte es sogar noch schlimmer sein. Ein Beispiel finden Sie hier http://stackoverflow.com/questions/13276862/cc-notation-of-double-floating-point -Werte/13279512 # 13279512 –

Antwort

4

Ihr "bester Weg" behauptet, dass Ihr generierter Code etwas innerhalb der float Fehlermarge, 7.1, zurückgibt. Dies kann sein, was Sie überprüfen möchten, in welchem ​​Fall, weitermachen.

Auf der anderen Seite, können Sie behaupten, dass Ihr generierter Code zurückgibt speziell das Ergebnis 7.1f zu einem double Gießen, in welchem ​​Fall man tun könnte:

Console.WriteLine(d == (double)f); 

Dies ist strenger - Ihr Test behauptet, dass d in einem kleinen Bereich liegt, während der obige Test behauptet, dass d ein bestimmter Wert ist.

Es hängt wirklich davon ab, was Sie verwenden werden d für. Wenn es einen Fall gibt, bei dem die Dinge schief gehen, wenn es nicht der genaue Wert ist, den genauen Wert testen, aber wenn es in Ordnung ist, innerhalb eines float des Wertes zu sein, überprüfen Sie gegen die float.

+0

Rawling, das ist es für mich. Ich erwarte, dass mein Code 7.1f in ein Double konvertiert, also lautet meine Behauptung nun d.Should(). Be ((double) 7.1f); das macht meine Erwartungen am deutlichsten. Danke für deine Eingabe. – citykid

1

zwei Schwimmer Punkt ibm sugestsabs(a/b - 1) < epsilon

ein msnd heißt es zu testen, Werte zu vergleichen, dass Epsilon Eigenschaft den kleinsten positiven Wert widerspiegelt, die in numerischen Operationen oder Vergleichen signifikant ist, wenn der Wert der Instanz Null ist.

so eigentlich sollten Sie

Math.Abs(d/(double)f) - 1) < float.Epsilon) 
+0

thx für den Hinweis von ibm: Wenn Sie die Skala der zugrunde liegenden Messungen nicht kennen, ist die Verwendung des Tests "abs (a/b - 1) citykid

+0

+1. Ja, es macht keinen Sinn, Epsilon zu einer möglicherweise großen Zahl hinzuzufügen. Es hätte überhaupt keinen Effekt. Dies würde nur für Festkommazahlen funktionieren. –

+0

Das Dokument, auf das Sie verweisen, wird von IBM veröffentlicht. Es wird jedoch von einer anderen Person und Firma als IBM als autorisiert identifiziert. Es ist also nicht klar, dass IBM diesen Vorschlag macht, mehr als ein Buchverleger mit allem einverstanden ist, was die Autoren veröffentlichen. Und es ist nicht ganz fair, IBM vorzuschreiben, diese schlampige Praxis zu fördern. –

1

(float) d == f überprüfen.

Eine andere Antwort vorgeschlagen d == (double) f, aber das ist ein nutzloser Test, weil (double) f führt die gleiche Konvertierung, die implizit d = f führt. Das einzige, was diese Behauptung testen könnte, ist, ob irgendein Aspekt der Implementierung gebrochen ist (z., der Compiler implementierte eine der Konvertierungen falsch und in einer Weise anders als die anderen), einige externe Mechanismus verändert oder f zwischen der Zuweisung und der Behauptung, oder der Quellcode wurden gebrochen, so dass d weder double noch float noch irgendein Typ das kann den Wert von f genau enthalten oder die Zuweisung d = f wurde nicht durchgeführt.

Im Allgemeinen erwarten wir keinen Gleitkommafehler, da bei jeder normalen Implementierung von Fließkommawerten die Konvertierung von einer schmaleren Genauigkeit in eine breitere Genauigkeit derselben Radix keinen Fehler hat, da die breitere Genauigkeit jeden Wert darstellen kann die engere Präzision kann. In ungewöhnlichen Situationen kann ein breiteres Gleitkommaformat einen kleineren Exponentenbereich haben. Nur in diesem Fall oder in perversen definierten Fließkommaformaten könnte die Konvertierung in ein breiteres Format eine Wertänderung bewirken. In diesen Fällen würde die Durchführung der gleichen Konvertierung die Änderung nicht erkennen.

Stattdessen konvertieren wir vom breiteren Format zurück in das schmalere Format. Wenn d sich von f unterscheidet, hat diese Konvertierung eine Chance, den Fehler zu erkennen. Z. B. angenommen f enthielt 0x1p-1000, aber aus irgendeinem Grund ist das nicht in dem Format d darstellbar, so dass es auf Null gerundet wurde. Dann (float) d == f wertet (float) 0 == 0x1p-1000 aus, dann zu 0 == 0x1p-1000, dann zu false. Außerdem kann dieser Test die gleichen Fehler wie der andere Vorschlag erkennen: eine fehlerhafte Implementierung, eine Änderung von d oder f, ein falscher Typ von d und eine fehlende Zuordnung von d = f.

Abgesehen davon, welche Fehler würden Sie hier mit einer Assertion erkennen?

+0

Es lohnt sich zu beachten, dass die Konvertierung von 'float' nach' Decimal' oder 'double' in' Decimal' verlustbehaftet * sein kann, selbst bei Werten, die in beiden Formaten genau dargestellt werden können *. Wenn Sie zum Beispiel "16777215f" in "Dezimal" umwandeln, erhalten Sie einen Wert von 16777220, obwohl "float" genau den Wert von 16.777.215 darstellt, und "Decimal" kann auch diesen Wert enthalten. – supercat

+0

@supercat: "... in jeder normalen Implementierung, Konvertierung ... zu einer größeren Genauigkeit ** der gleichen Radix ** hat keinen Fehler ...". –

+0

Es ist wahr, dass man nicht erwarten kann, dass jeder 'float'-Wert * in 'Decimal' * darstellbar ist, da sie unterschiedliche Radix verwenden, und man kann sicherlich nicht erwarten, dass eine Umwandlung genau ist, wenn das Zielformat keine Repräsentation hat für den Wert in Frage. Meine Absicht war, nicht zu widersprechen, sondern zu betonen, dass, wenn sich Radixes unterscheiden, sogar Werte, die in alten und neuen Formaten präzise darstellbar sind, sich merkwürdig umwandeln (vielleicht ist die Rundung von 16777215f auf 16777220m dokumentiert, aber es scheint seltsam zu sein)). – supercat