Nieoczekiwany wynik odejmowania

Nieoczekiwany wynik odejmowania
LI
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 36
0

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

Kopiuj
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:

Kopiuj
cout << decimalPointsCount(0.53);

dostaję taki output:

Kopiuj
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ć.

SI
  • Rejestracja: dni
  • Ostatnio: dni
0

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

AK
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 3561
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

MarekR22
  • Rejestracja: dni
  • Ostatnio: dni
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?

SI
  • Rejestracja: dni
  • Ostatnio: dni
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)

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.