MZG Opublikowano 21 Listopada 2012 Udostępnij Opublikowano 21 Listopada 2012 Witam. Ostatnio czytałem co nieco o typach z ułamkowych i postanowiłem sam sprawdzić kilka kwestii. Słabą dokładność przy dużych liczbach mogę wybaczyć, ale bardzo nietypowego błędu zwykłego dodawania już nie. O co mi chodzi? O to kod obrazujący to doskonale: #include <iostream> #include <iomanip> using namespace std; int main() { double a = 0; //moze byc zarowno float czy long double, efekt jest ten sam cout << a; cout << setprecision(32); //ustalanie precyzji ulamka dziesietnego for(;;) { a += 0.001; cout << a; cout << endl; } } Ten kod powinien dodawać nieskończenie 0.001 zaczynając od zera. I to robi, jednak dzięki funkcji "setprecision" można spojrzeć ukradkiem na część która jest zwykle ukryta. Mianowicie w po tej dodawanej części tysięcznej następuje ciąg zer, i na końcu też pojawiają się cyfry. Ale w kodzie nie dodawane są takie małe ułamki! Porównać to można do tego, że komputer niedokładnie dolewa wody do dzbanka zmiennej "a" że są te "nierówności". Co więcej, po wcale nie długim czasie ten błąd pomiarowy wpływa na wynik. Zamiast "1" dodaje jakimś cudem "0.9999..." i już jest strata jednej wartości. To może być wąż boa wychodowany na gardle jeśli próbujemy wewnętrznie liczyć czas w grze takim typem(bo potrzebowałby dokładności). Dziwi mnie taka kolej rzeczy, przecież komputery(a dokładnie procesory0 powinny być idealnie dokładne. Czym może być spowodowany ten błąd? Może to przez funkcję set precision(w co bardzo wątpie)? Wiem że nie jest to może jakiś critical error który przeszkadza w kompilowaniu programuale trochę mnie to irytuje że do takich zmiennych szczycących się wysoką dokładnością obliczeń po przecinku może wbić się taki błąd... I jestem ciekawy jaki jest tego powód :twisted: Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
Shockah Opublikowano 21 Listopada 2012 Udostępnij Opublikowano 21 Listopada 2012 Bo tak działają floaty / double. Nie lepiej dzielić na bieżąco dwóch wartości (np. typu int) przez siebie (uprzednio zamieniając je na liczby zmiennoprzecinkowe) by uzyskać wynik? Będzie on zawsze jak najbardziej możliwie dokładny. Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
MZG Opublikowano 21 Listopada 2012 Autor Udostępnij Opublikowano 21 Listopada 2012 O coś takiego Ci chodzi?: int a = 1000; int b = 1; for(;;){ cout << (static_cast<float>(b) / static_cast<float>(a)); --a; cout << endl; } Spróbowałem i wychodzą dużo gorsze błędy i większe różnice od prawdziwej wartości która powinna być. :/ Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
Shockah Opublikowano 21 Listopada 2012 Udostępnij Opublikowano 21 Listopada 2012 Nie wiem, nie bawię się C++ za bardzo, ale w Javie wyglądałoby to tak: for (int i = 0; i <= 1000; i++) System.out.println(i/1000f); Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
MZG Opublikowano 21 Listopada 2012 Autor Udostępnij Opublikowano 21 Listopada 2012 Ja za to za javą zbytnio nie przepadam ;P. Ale uniwersalne zdolności programistyczne i to że jednak na wszelki wypadek mam kompilator również do javy to pozwoliłem sobie to sprawdzić i racja, w javie nie ma tego błędu. Nawet skopiowałem skrypt żeby było 30 liczb po przecinku i tak wszystko dokładnie obliczone. W takim razie czemu w C++ się to rypie? :mellow: Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
Shockah Opublikowano 21 Listopada 2012 Udostępnij Opublikowano 21 Listopada 2012 Możesz spróbować to zrobić tak jak ja - przy dzieleniu miej pewność, że choć jedna z wartości jest floatem. Pomnóż przez floata 1, dodaj floata 0... Cokolwiek. A przynajmiej tak mi się wydaje, że C++ działa tak samo pod tym względem. Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
MZG Opublikowano 21 Listopada 2012 Autor Udostępnij Opublikowano 21 Listopada 2012 Nie wiem jak miałoby to działać. Tu właśnie chodzi że floaty nawalają. Kiedy do floata dodaje 0.001 ciągle to wychodzi coś takiego(nie idealnie tak samo, ale na tej zasadzie): 0.00100000000000000001 0.00200000000000000002 0.00300000000000000004 . . . 0.45600000000000000757 0.45699999999999999987 Bardzo dziwny błąd. Tak jakby komputer "niedokładnie dolewał" co już wspomniałem. A czy wszelako różnych konwersjach sprawa się pogarsza. W dodatku napisałem prosty kod if(static_cast<int>(i/0.001) != 0) //obliczanie reszty dzielenia z floata continue; //jezeli reszta nie jest rowna zero to wroc do poczatku petli Jedyne co zobaczyłem to ekran konsoli z jednym zerem. [EDIT] Meh, znalazłem powód tego stanu rzeczy. Te liczby napisanie binarnie są nieskończone, więc komputer nie może ich "skończyć" dziesiętnie. Wychodzi na to że jedynym ratunkiem jest zaokrąglanie. Ale dziwne jest to że w javie tego nie ma. Przecież tam też się oblicza binarnie... Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
Administratorzy gnysek Opublikowano 22 Listopada 2012 Administratorzy Udostępnij Opublikowano 22 Listopada 2012 W Javie jest bodaj taki błąd, że jak do maksymalnej wartości doubla dodasz 1, to uzyskasz ujemną liczbę. A przynajmniej taką na studiach słyszałem historię, że właśnie gdzieś tam nie wzięto tego pod uwagę i się system sypnął i tydzień szukali przyczyny. Nie wiem czy to nie przy okazji projektowania klasy, która przetrzymuje ułamki zwykłe i tworzy wspólne mianowniki ;) Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
Will Opublikowano 22 Listopada 2012 Udostępnij Opublikowano 22 Listopada 2012 http://docs.oracle.com/cd/E19957-01/806-35...g_goldberg.html http://www.cygnus-software.com/papers/comp...aringfloats.htm Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
Rekomendowane odpowiedzi
Jeśli chcesz dodać odpowiedź, zaloguj się lub zarejestruj nowe konto
Jedynie zarejestrowani użytkownicy mogą komentować zawartość tej strony.
Zarejestruj nowe konto
Załóż nowe konto. To bardzo proste!
Zarejestruj sięZaloguj się
Posiadasz już konto? Zaloguj się poniżej.
Zaloguj się