double on the floor

Krótko ilustrowana opowieść o tym, jak skomplikowana bywa praca programisty:

Prosta(?) operacja na typie double w eMbedded Visual C++ 4.0

Wyjaśnienie dodatkowe: zmienne n i x są typu double, fact to int.

Wyjaśnienie dodatkowe dla mało widzących: wg kompilatora Microsoft eMbedded Visual C++ 4.0 zmienna typu double o wartości 538,06 (i nie tylko, wczoraj w jednej chwili poznałem 16 wartości o takiej właściwości) pomnożona przez 10000 i obcięta w dół do liczby całkowitej daje w wyniku liczbę 5380599,0.

PS: Niestety przypadek ten wyśledziłem w autentycznym, działającym systemie. Na szczęście nie jest to system naprowadzania rakiet. Na obrazku powyżej nazwa projektu została wykreślona w celu ochrony niewinnych...

Komentarze

#1 | 2005.04.15 09:00 | Łukasz Grabuń

Na rysunku widać w jaki sposób wyświetlana jest wartość zmiennej n (na boczku: co to za zwyczaj używania litery N do oznaczenia nie liczby naturalnej, a quasi-rzeczywistej :-). Próbowałeś wypisać na wyjściu wartość tej zmiennej? Być może w istocie wartość wynosi 538.0599000?

Jeśli jednak to błąd kompilatora... Uch, potworny babol. Przypomina mi moją zabawę z fakturą.

#2 | 2005.04.15 09:48 | MiMaS

Hmmm, dobrze kombinujesz, ale niestety nie masz racji... Obie zmienne n i x są wyświetlanie przez debugger zgodnie z faktycznymi wartościami. Na pewno.

Może jeszcze słowo na temat otoczenia: rzecz odbywa się na PDA (PocketPC 2003) i dotyczy zapisu do bazy danej pobranej z serwera. Zapis odbywa się za pośrednictwem biblioteki napisanej w eMbedded C++ z powodów ... powiedzmy wydajnościowych. Na serwerze jest na pewno wartość 538,0600 i taka też jest wartość zmiennej n. W bazie SQLCE na PDA jest zapisywane 538,0599 — wynik wartości pośredniej zmiennej x. Sprawdzone na 100%.

#3 | 2005.04.20 13:10 | gshegosh

Nie chciałbym się specjalnie wymądrzać, ale reprezentacja liczb w systemie dwójkowym potrafi sprawiać takie niespodzianki - dla przykładu dziesiętnie mamy 0,1 ale gdy próbujemy tę liczbę przedstawić dwójkowo - okaże się, że rozwinięcie jest nieskończone (podobnie jak w systemie dziesiętnym nieskończone jest rozwinięcie 1/3). A przecież double jest trzymany w skończonej liczbie bitów... I problem z zaokrągleniami gotowy.
Na zaokrąglenia jest nawet standard IEEE (http://www.math.byu.edu/~schow/work/IEEEFloatingPoint.htm).
Rozwiązaniem jest stosowanie intów (real programmers use integers ;) lub też klas typu BigDecimal w Javie...

#4 | 2005.04.20 14:10 | MiMaS

przecież double jest trzymany w skończonej liczbie bitów... I problem z zaokrągleniami gotowy.

Bingo :-)

Jednak nie zmienia to faktu, że praca programisty bywa bardziej skomplikowana niż się wielu wydaje. Co więcej, z obserwacji rzeczywistości wnioskuję, że mało kogo obchodzą takie pierdoły jak precyzja/reprezentacja liczby... Efekt — wspomiane 16 przypadków na raz. Tylko, że tym razem to już się nie nazywało „niespodzianki” tylko „źle zapisane pozycje faktur”...

Rozwiązaniem jest stosowanie intów

A jak zamienisz na int skoro pomnożenie przez inta (fact w przykładzie) już powoduje błąd..? ;-)

lub też klas typu BigDecimal w Javie...

Fajnie. Tylko niestety to jest eMVC++ a nie Java...

#6 | 2005.06.23 05:19 | sprae

W gcc natomiast wychodzi wartosc "poprawna" - 5380600.00

 

Uwaga: Ze względu na bardzo intensywną działalność spambotów komentowanie zostało wyłączone po 60 dniach od opublikowania wpisu. Jeżeli faktycznie chcesz jeszcze skomentować skorzystaj ze strony kontaktowej.