2012-12-19 12 views
17

Biorąc pod uwagę oświadczeniaKonwersja Float na podwójną - Najlepsza asercja w teście jednostkowym?

float f = 7.1f; 
double d = f; 

Co możemy dochodzić w badanej jednostki o d?


Na przykład to nie działa:

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) 

Najlepszym sposobem znalazłem tak daleko jest do konwersji wartości powrotem:

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

Która byłaby taka sama jak brute sposób na powiedzenie:

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

To pytanie NIE dotyczy dokładności double i float, ale naprawdę JUST o pragmatycznym pytaniu, w jaki sposób test jednostkowy najlepiej opisuje ograniczenia d. W moim przypadku d jest wynikiem konwersji, która występuje w kodzie generowanym przez generowanie kodu lekkiego. Podczas testowania tego generowania kodu muszę przedstawić twierdzenia dotyczące wyniku tej funkcji, co ostatecznie sprowadza się do prostego pytania powyżej.

+0

Z jakiego schematu testów jednostkowych korzystasz? Niekoniecznie możesz twierdzić przeciwko warunkowi, który wyprowadzasz np. 'Assert.IsTrue (warunek)', możesz być w stanie użyć np. 'Assert.AreEqual (value1, value2)' który może obsługiwać równoważność między formatami numerycznymi. – StuperUser

+0

Nadal nie jestem pewien, co próbujesz przetestować tutaj. Podwójne ma większą precyzję niż pływak, a zatem prawie nigdy nie będą "równe" sobie nawzajem. – CodeCaster

+0

Jeśli pominiesz końcową wartość f stałej używanej do inicjowania float, może być jeszcze gorzej, zobacz przykładowy skrypt tutaj http://stackoverflow.com/questions/13276862/cc-notation-of-double-floatingpointpoint -values ​​/ 13279512 # 13279512 –

Odpowiedz

4

Twój "najlepszy sposób" oznacza, że ​​wygenerowany kod zwraca wartość, która jest z zakresu błędu float, 7.1. To może być to, co chcesz sprawdzić, w takim przypadku, kontynuuj.

Z drugiej strony, można twierdzić, że wygenerowany kod zwraca specjalnie roku wynik odlewania 7.1f do double, w którym to przypadku można zrobić:

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

Jest to bardziej rygorystyczne - Twój test potwierdza, że ​​d mieści się w niewielkim zakresie, podczas gdy powyższy test potwierdza, że ​​d ma określoną wartość.

To naprawdę zależy od tego, do czego będziesz używał d dla. Jeśli jest to przypadek, w którym rzeczy pójdą źle, jeśli nie jest to dokładna wartość, przetestuj dokładną wartość, ale jeśli jest w porządku, aby znaleźć się w wartości float, sprawdź w stosunku do float.

+0

Rawling, to jest to dla mnie. Oczekuję, że mój kod skonwertuje 7.1f na podwójne, więc moje potwierdzenie to teraz: d.Should(). Be ((podwójne) 7.1f); To sprawia, że ​​moje oczekiwania są najbardziej jasne. thx za twoje wejście. – citykid

1

Aby porównać dwa punkt pływak wartości ibm sugests przetestować abs(a/b - 1) < epsilon

msnd stwierdza, że ​​Epsilon nieruchomość odzwierciedla najmniejszą dodatnią wartość jest istotna w operacji numerycznych lub porównań, gdy wartość instancji wynosi zero.

tak faktycznie należy sprawdzić

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

thx za podpowiedź od ibm: Jeśli nie znasz skali podstawowych pomiarów, użycie testu "abs (a/b - 1) citykid

+0

+1. Tak, nie ma sensu dodawać epsilon do możliwie dużej liczby. Nie przyniosłoby to żadnego skutku. To działałoby tylko dla stałych numerów punktów. –

+0

Dokument, na który wskazuje użytkownik, jest publikowany przez IBM, ale został zidentyfikowany jako autor i osoba prawna inna niż IBM. Nie jest więc jasne, czy IBM robi taką sugestię, niż wydawca książek zgadza się ze wszystkim, co autorzy publikują. I nie jest w pełni uczciwe oskarżanie IBM o promowanie tej niedbałej praktyki w oparciu o to. –

1

(float) d == f.

Kolejna odpowiedź zasugerowana d == (double) f, ale jest to bezużyteczny test, ponieważ (double) f wykonuje tę samą konwersję, którą wykonuje domyślnie d = f. Tak więc jedyną rzeczą, jaką może być to stwierdzenie, jest to, czy jakiś aspekt implementacji jest zepsuty (np.kompilator zaimplementował jedną z konwersji niepoprawnie i w sposób odmienny od innego), część zewnętrznego mechanizmu zmienionego między przydziałem a asercją lub kod źródłowy zostały zerwane, aby d nie było ani double ani aniani aniktóry może przechowywać wartość f dokładnie lub zadanie d = f nie zostało wykonane.

Ogólnie rzecz biorąc, nie oczekujemy żadnego błędu zmiennoprzecinkowego, ponieważ przy każdej normalnej implementacji zmiennoprzecinkowej konwersja z węższej precyzji na większą dokładność tego samego prostokąta nie powoduje błędów, ponieważ szersza precyzja może reprezentować każdą wartość węższa precyzja może. W nietypowych sytuacjach szerszy format zmiennoprzecinkowy może mieć mniejszy zakres wykładników. Tylko w tym przypadku lub w przewrotnie zdefiniowanych formatach zmiennoprzecinkowych konwersja do szerszego formatu może spowodować zmianę wartości. W takich przypadkach wykonanie tej samej konwersji nie wykryłoby zmiany.

Zamiast tego, konwertujemy z szerszego formatu z powrotem na węższy format. Jeśli d różni się od f, konwersja ta ma szansę na wykrycie błędu. Np. Załóżmy, że f zawierał 0x1p-1000, ale z jakiegoś powodu nie można go przedstawić w formacie d, więc zostało zaokrąglone do zera. Następnie (float) d == f ocenia się na (float) 0 == 0x1p-1000, a następnie na 0 == 0x1p-1000, a następnie na false. Ponadto test ten może wykryć te same błędy, co druga sugestia: złamanie implementacji, zmiana d lub f, niepoprawny typ d i brakujące zadanie d = f.

Poza tym, jakie błędy chciałbyś wykryć tutaj za pomocą asercji?

+0

Warto zauważyć, że konwersja z 'float' na' Decimal' lub 'double' na' Decimal' może być stratna * nawet dla wartości, które są dokładnie reprezentowane w obu formatach *. Na przykład konwertowanie '16777215f' na' Dziesiętny' daje wartość 16777220, mimo że 'float' precyzyjnie reprezentuje wartość 16 777 2115, a" Dziesiętny "może również utrzymywać tę wartość. – supercat

+0

@supercat: "... w każdej normalnej implementacji konwersja ... na większą precyzję ** tego samego radix ** nie powoduje błędu ...". –

+0

To prawda, że ​​nie można oczekiwać, że każda wartość 'float' będzie * reprezentowalna * w' Dziesiętnym', biorąc pod uwagę, że używają one innej podstawy, a na pewno nie można oczekiwać, że konwersja będzie dokładna w przypadkach, gdy docelowy format nie ma reprezentacji dla danej wartości. Moją intencją było nie zaprzeczyć, ale raczej podkreślić, że jeśli radixy różnią się, nawet wartości, które są dokładnie reprezentowalne w starych i nowych formatach, mogą się dziwnie przekształcić (być może udokumentowane jest zaokrąglenie 16777215f do 16777220m, ale wydaje się dziwne co najmniej). – supercat