2008-10-30 19 views
25

Słyszałem o "błędzie" podczas używania zmiennych zmiennoprzecinkowych. Teraz próbuję rozwiązać tę zagadkę i myślę, że otrzymuję błąd zaokrąglania/zmiennoprzecinkowy. W końcu odkryję podstawy błędu zmiennoprzecinkowego.Jaki jest prosty przykład błędu zmiennoprzecinkowego/zaokrąglania?

Jaki jest prosty przykład błędu zmiennoprzecinkowego/zaokrąglania (najlepiej w C++)?

Edycja: Na przykład powiedz, że mam zdarzenie, które ma prawdopodobieństwo sukcesu. Robię to wydarzenie 10 razy (p nie zmienia się, a wszystkie próby są niezależne). Jakie jest prawdopodobieństwo dokładnie 2 udanych prób? Mam kodowane jako:

double p_2x_success = pow(1-p, (double)8) * pow(p, (double)2) * (double)choose(8, 2); 

Czy jest to szansa na błąd zmiennoprzecinkowy?

+0

myślę, czego naprawdę potrzebujesz, to w ten sposób: [Co każdy komputer naukowiec powinien wiedzieć o arytmetyki zmiennoprzecinkowej] (http://docs.sun.com/source/ 806-3568/ncg_goldberg.html). – Patrick

+0

Przeczytaj to: http://blog.frama-c.com/index.php?post/2013/05/02/nearbyintf1 –

+0

Zobacz prosty przykład Java, shuld być taki sam w C: http://stackoverflow.com/ a/15790782/254109 – xmedeko

Odpowiedz

9

Zasadniczo błąd zmiennoprzecinkowy odnosi się do liczby, której nie można zapisać w reprezentacji zmiennoprzecinkowej IEEE.

Liczby całkowite są przechowywane z najbardziej na prawo wybranym bitem 1, a każdy bit po lewej jest podwójny (2,4,8, ...). Łatwo zauważyć, że może to przechowywać dowolną liczbę całkowitą do 2^n, gdzie n to liczba bitów.

Mantysa (część dziesiętna) liczby zmiennoprzecinkowej jest przechowywana w podobny sposób, ale porusza się od lewej do prawej, a każdy kolejny bit stanowi połowę wartości poprzedniej. (W rzeczywistości jest to trochę bardziej skomplikowane, ale na razie to zrobi).

W ten sposób liczby takie jak 0,5 (1/2) są łatwe do zapisania, ale nie każdy numer < 1 może być utworzony przez dodanie stałej liczby ułamków postaci 1/2, 1/4, 1/8, ...

Bardzo prostym przykładem jest 0,1 lub 1/10. Można to zrobić za pomocą nieskończonej serii (której nie da się naprawdę wyleczyć), ale gdy komputer przechowuje 0.1, to nie jest to dokładnie ta liczba, która jest przechowywana.

Jeśli masz dostęp do maszyny Unix, to łatwo zobaczyć.

Python 2.5.1 (r251:54863, Apr 15 2008, 22:57:26) 
[GCC 4.0.1 (Apple Inc. build 5465)] on darwin 
Type "help", "copyright", "credits" or "license" for more information. 
>>> 0.1 
0.10000000000000001 
>>> 

Będziemy chcieli, aby być naprawdę ostrożny z testów równości z pływaków i deblu, w jakimkolwiek języku jesteś w

(Dla twojego przykładu, 0.2 to kolejna z tych brzydkich liczb, których nie można zapisać w pliku binarnym IEEE, ale dopóki testujesz nierówności, a nie równości, np. P < = 0.2, wszystko będzie w porządku .)

5

Prosty C jeden który caug ht mnie czas temu

char *c = "90.1000"; 
double d = 0; 
sscanf(c,"%f",&d); 
printf("%0.4f",d); 

>> 90.0999 

Było to w funkcji kąta, który przekształcany w DMS radianach, co nie w powyższym przykładzie.

+2

Jak zauważył anonimowy użytkownik, w 'sscanf' specyfikator konwersji" f "wymaga argumentu' float', a nie 'double' (jednak" f "oznacza' double' na 'printf' - - tak, jest mylące). Zmiennik konwersji zmodyfikowany "lf" powinien być użyty, aby 'sscanf' działał z' podwójnym'. –

26
for(double d = 0; d != 0.3; d += 0.1); // never terminates 
19

obraz jest wart tysiąca słów - staramy się wyciągnąć równanie f(k):
enter image description here
a dostaniesz takiego wykresu XY (X i Y są w skali logarytmicznej).
enter image description here
Jeśli komputer może reprezentować 32-bitowe zmienne bez błędu zaokrągleń, to za każde k powinniśmy otrzymać zero. Ale zamiast tego błąd rośnie wraz z większymi wartościami k z powodu kumulacji błędów zmiennoprzecinkowych.

hth!

3

Oto jeden, który mnie złapał.

round(256.49999) == 256 
roundf(256.49999) == 257 

dwuosobowe i pływa ..