Problem z konwersja string na double

0

#include <bits/stdc++.h>

using namespace std;
long double s;
int main()
{
    string n;

    int t;
    cin>> t;
    while(t--)
    {
    s=0;
    cin>> n;

    for(int i=0; i<n.size(); i++)
    s+=(n[i]-48)*pow(10, n.size()-i-1);

    cout<<setprecision(n.size())<< s;
    }
    return 0;
}```
Czemu ten program powyzej liczb 10^19 dodaje sobie cos do zmiennej s? Z gory dzieki za pomoc!
0

Czemu ten program powyzej liczb 10^19 dodaje sobie cos do zmiennej s?

Co to jest to coś? Dlaczego nie wkleiłeś outputu?

0

Program poglądowy:

#include <iostream>
#include <cmath>
#include <string>
#include <iomanip> // setprecision
void convert(std::string& s) {
    long double num = 0;
    for (size_t i = 0; i < s.size(); i++) {
        int digit = s[i] - '0';
        double p = pow(10, s.size() - i - 1);
        std::cout << digit <<  " " << std::setprecision(s.size()) << p << std::endl;
        num += digit * pow(10, s.size() - i - 1);
        std::cout << std::setprecision(s.size()) << num << std::endl;
    }
}
int main() {
    std::string s = "1";
    int t = 22;
    for (int i = 0; i < t; i++) {
        s += static_cast<char>(i % 10 + '0');
        std::cout << "string: " << s << "  len: " << s.size() << std::endl;
        convert(s);
    }

}

oraz przykładowy output z widocznym problemem:

string: 101234567890123456789 len: 21
1 100000000000000000000
100000000000000000000
0 10000000000000000000
100000000000000000000
1 1000000000000000000
101000000000000000000
2 100000000000000000
101200000000000000000
3 10000000000000000
101230000000000000000
4 1000000000000000
101234000000000000000
5 100000000000000
101234500000000000000
6 10000000000000
101234560000000000000
7 1000000000000
101234567000000000000
8 100000000000
101234567800000000000
9 10000000000
101234567890000000000
0 1000000000
101234567890000000000
1 100000000
101234567890100000000
2 10000000
101234567890120000000
3 1000000
101234567890123000000
4 100000
101234567890123400000
5 10000
101234567890123450000
6 1000
101234567890123456000
7 100
101234567890123456704
8 10
101234567890123456784
9 1
101234567890123456792

W moim przykładzie akurat problem widać od długości 20 dla danej liczby. Na standardowym PCie x86_64 typ long double powinien być reprezentowany przez 80-bitowy typ zmiennoprzecinkowy znany z koprocesorów x87. W takim formacie (za Wikiepią) taki format ma reprezentację mantysy na 64 bitach. Zgodnie z tym, zakres dla 64-bitowej liczby kończy się przy wartościach ok. 10^19.
W tym programie trafiamy na taki problem reprezentacji:

6 1000
101234567890123456000
7 100
101234567890123456704

Kiedy do dużej liczby zmiennoprzecinkowej dodajemy małą, jej wartość w wyniku sumy ginie ze względu na ograniczoną reprezentację, jednocześnie jest to ułamek, który już nie przedstawia liczby ze skończoną binarną reprezentacją, stąd "błędny" wynik na ostatnich cyfrach wypisywanych liczb.

0
Lycos napisał(a):
s+=(n[i]-48)*pow(10, n.size()-i-1);

Dlaczego strzelasz z armaty, tj z funkcji pow, mnożenie przez 10.0 jest wystarczające i ma maksymalną osiągalną dokładność (czego o pow() nie da się powiedzieć - ona jest dostosowana do ułamkowych potęg, i najstarsi górale nie wiedzą, co robi po drodze. O szybkość nawet nie pytam)

Algorytm konwersji, taki, jaki ma być po bożemu, jest całkiem wdzięczny - tu jest jakaś pokraka.
I stała 48 rani moje oczy.

1

Liczby zmiennoprzecinkowe, mają skończoną precyzję. Jedynie określona liczba cyfr wiodących jest odwzorowana, reszta jest zaokrąglana.

Pewnie jesteś "wychowankiem" Zelent-a.

0
MarekR22 napisał(a):

Liczby zmiennoprzecinkowe, mają skończoną precyzję. Jedynie określona liczba cyfr wiodących jest odwzorowana, reszta jest zaokrąglana.

Gdyby użyć metody Hornera po bożemu, to być może by nie było aż tak wiele śmieci, gdyż metoda Hornera jest numerycznie stabilna, w przeciwieństwie do sumowania takich potęg.

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