Nieoczekiwany wynik odejmowania

0

Cześć,
Napisałem funkcję, która w założeniu miała liczyć ilość miejsc po przecinku w zmiennej double:

int decimalPointsCount(double number)
{
    number -= long(number);

    int res = 0;

    while((number - (long long)number) != 0)
    {
        cout << number << " - " << (long)number << " = " << (number-(long)number) << endl;
        ++res;
        number *= 10;
    }

    return res;
}

Po wywołaniu funkcji:

cout << decimalPointsCount(0.53);

dostaję taki output:

0.53 - 0 = 0.53
5.3 - 5 = 0.3
53 - 53 = 7.10543e-015
530 - 530 = 1.13687e-013
5300 - 5300 = 9.09495e-013
53000 - 53000 = 7.27596e-012
530000 - 530000 = 1.16415e-010
5.3e+006 - 5300000 = 9.31323e-010
5.3e+007 - 53000000 = 7.45058e-009
5.3e+008 - 530000000 = 5.96046e-008
5.3e+009 - -2147483648 = 7.44748e+009
5.3e+010 - -2147483648 = 5.51475e+010
5.3e+011 - -2147483648 = 5.32147e+011
5.3e+012 - -2147483648 = 5.30215e+012
5.3e+013 - -2147483648 = 5.30021e+013
5.3e+014 - -2147483648 = 5.30002e+014

Dlaczego po trzeciej iteracji pętla się nie zakończyła, skoro odejmowane od siebie liczby były sobie równe? Czemu wynik odejmowania to 7.10543e-015? Po tym, co znalazłem w internecie wnioskuję, że w momencie mnożenia number przez 10 zachodzi strata danych, ale nie potrafię tego problemu rozwiązać.

0

Ze względu na sposób przechowywania liczb "z przecinkiem" w komputerze, obliczenia na nich nigdy nie są wiarygodne.

4
sig napisał(a):

Ze względu na sposób przechowywania liczb "z przecinkiem" w komputerze, obliczenia na nich nigdy nie są wiarygodne.

Są wiarygodne, a wiara opiera się na umiejętności odróżnienia co można, i co nie można oczekiwać.
Rachunek błędów jest czymś normalnym w zawodach inżynierskich. Trzeba to znać/umieć a nie wysuwać jakiś spiskowych teorii.

Dwa główne problemy ze zmiennym przecinkiem, to błąd z ograniczonej dokładności (conieco ludowi znany), i błąd reprezentacji (znany znacznie mniej a bardziej zaskakujący)
Look wikipedia

2

tak jak 1/3 nie ma skończonego dokładnego zapisu w systemie dziesiętnym 0.33333333 tak wiele liczb (jak 0.3 albo 0.53) nie ma dokładnej reprezentacji w systemie binarnym.
W efekcie liczby muszą być zaokrąglone do precyzji danego typu zmiennoprzecinkowego.
Zaokrąglenia oznacza błędy, a błędy oznaczają, że wynik nie jest dokładny, ergo zero nie musi w wyniku być dokładnie zero.

Swoją drogą twoje pytanie zapewne ma naturę problemu XY.
Jaki problem ma rozwiązywać twój kod?

1

Próbuj FP_ZERO z https://en.cppreference.com/w/cpp/numeric/math/FP_categories, jak nie pomoże to już chyba tylko arytmetyka stringowa albo działanie na liczbach całkowitych, przecinek gdzie trzeba dopiszesz przy wyświetlaniu (np banki liczą na groszach, żeby uniknąć twojego problemu)

1 użytkowników online, w tym zalogowanych: 0, gości: 1