Skocz do zawartości

Dziwne zachowania typów zmiennoprzecinkowych


MZG

Rekomendowane odpowiedzi

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

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

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

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

  • Administratorzy

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

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ę
  • Ostatnio przeglądający   0 użytkowników

    • Brak zarejestrowanych użytkowników przeglądających tę stronę.
×
×
  • Dodaj nową pozycję...