Skocz do zawartości

Witaj!

Zaloguj lub Zarejestruj się aby uzyskać pełny dostęp do forum.

Zdjęcie
- - - - -

C++ duże liczby, brak dokładności/zaokrąglanie


  • Zaloguj się, aby dodać odpowiedź
7 odpowiedzi w tym temacie

#1 1mysliwy1

1mysliwy1
  • 99 postów

Napisano 05 września 2011 - 17:32

Witam!

Pisząc program natrafiłem na poważny problem, dla mnie nie do przejścia.

Opiszę co program robi:
program najpierw wykonuje pewne operacje na liczbach a na końcu wynik pierwiastkuje i wypisuje. Potem również operuje na wyniku ale mniejsza o to.

Problem pojawia się właśnie przy pierwiastkowaniu, proszę spójrzcie na ten przykładowy program pokazujący mój problem. Co jest nie tak?
załączone biblioteki to i . Jak normalnie wkleić ten kod tutaj???

#include
#include
using namespace std;
int main () {
double liczba=(pow(5,10)+3); //dwie zupełnie inne liczby, ten sam wynik po pierwiastkowaniu
double liczba2=(pow(5,10)+0);
cout<<
liczba<<"\n";
cout<<
sqrt(liczba)<<"\n";
cout<<
liczba2<<"\n";
cout<<
sqrt(liczba2)<<"\n";
return 0;
}

Wydruk z konsoli:

9.76563e+06
3125
9.76562e+06
3125


Chodzi o problem przy dużych liczbach, np. wyświetla: 1.49012e+18. Jak zrobić, żeby wyświetlało mi normalnie? Właśnie przypuszczam, że z tego wynikają potem błędy w wyliczeniach i wszystko się sypie.

#2 konrado0905

konrado0905
  • 361 postów

Napisano 05 września 2011 - 18:37



#include
#include

using namespace std;

int main ()
{
[INDENT]double liczba=(pow(5,10)+3); //dwie zupełnie inne liczby, ten sam wynik po pierwiastkowaniu
double liczba2=(pow(5,10)+0);

[B]cout.fixed;[/B]

cout << liczba << endl;
cout << sqrt(liczba) << endl;
cout << liczba2 << endl;
cout << sqrt(liczba2) << endl;

return 0;[/INDENT]
}


Zobacz czy będzie działać. Pracuj nad czytelnością kodu.

#3 Krzema

Krzema
  • 190 postów
  • SkądGdańsk, PL

Napisano 05 września 2011 - 20:05

Koledze wyżej chodziło pewnie o
cout.flags(ios::fixed);

Co do błędów w obliczeniach, tryb wyświetlania nie ma tu nic do rzeczy. Strumień cout stara się być inteligentny i czasami za dużo zaokrągli. Poza tym zapis wykładniczy (typu "1.49012e+18") jest całkiem przyjemny, w normalnym zapisie ta liczba wyglądałaby mniej więcej tak: 1490120000000000000.
Wszelkie niedokładności wynikają ze specyfiki formatu przechowywania liczb. Gdzieś tam jego dokładność się kończy. Zresztą można sobie policzyć dokładność Double precision floating-point format - Wikipedia, the free encyclopedia .

#4 konrado0905

konrado0905
  • 361 postów

Napisano 05 września 2011 - 20:25

@Krzema: Możliwe. Już się trochę pozapominało ;)

#5 1mysliwy1

1mysliwy1
  • 99 postów

Napisano 05 września 2011 - 22:21

cout.flags(ios::fixed); działa przy tych danych co były w pierwszym programie, ale jak dam już (pow(2,60)+5) lub (pow(2,60)+11) to dla niego znów to jest tym samym. Macie jakieś rady jak to zaradzić?

#6 Tojot

Tojot
  • 1 187 postów

Napisano 05 września 2011 - 22:40

Powiedz co tak na prawdę chcesz zrobić, bez tego trudno coś doradzić. W ogólnym przypadku, to jak dla mnie, to wszystko działa tak jak należy.

---------- Wpis dodano o 22:40 ---------- Poprzedni wpis dodano o 22:39 ----------

Zarządzanie błędem zaokrąglenia to dziedzina sama w sobie.

#7 Krzema

Krzema
  • 190 postów
  • SkądGdańsk, PL

Napisano 06 września 2011 - 17:27

cout.flags(ios::fixed); działa przy tych danych co były w pierwszym programie, ale jak dam już (pow(2,60)+5) lub (pow(2,60)+11) to dla niego znów to jest tym samym. Macie jakieś rady jak to zaradzić?

Rada jest na to taka, że musisz zrozumieć jak działa double ;) Obie te liczby, które podałeś, w ogóle nie różnią się gdy zostaną zapisane do double'a. Obie - przed spierwiastkowaniem - mają wartość 0x000000000000B043. To co tam dodajesz (5, a później 11) jest zbyt małe rzędem w stosunku do 2^60 żeby jakkolwiek wpłynęło na liczbę (w formacie double).
Tu masz kod wypisujący daną liczbę w binarze. Pobaw się trochę, spróbuj np. dodać taką liczbę do tej 2^60, która zmieni najmłodszy bit liczby w double'u. To się da wszystko wyliczyć, ale nie ma to jak praktyka. Poza tym jeszcze raz proponuję poczytać na wiki o formatach zmiennoprzecinkowych.

#include 
#include
#include
using namespace std;

int main()
{
double liczba = pow(2, 60) + 11;
double pierw = sqrt(liczba);

char buffer[8];
memcpy(buffer, &pierw, 8);

for(int i=0; i<8; i++)
cout << hex << (unsigned int)(buffer[i]&0xFF) << endl;
}


A jeżeli potrzebujesz tak ultradokładnych obliczeń, to pewnie są odpowiednie klasy. Jak bardzo się upierasz, to jeszcze spróbuj long double.

#8 bartek1983

bartek1983
  • 18 postów

Napisano 12 października 2011 - 12:15

Chodzi o problem przy dużych liczbach, np. wyświetla: 1.49012e+18. Jak zrobić, żeby wyświetlało mi normalnie? Właśnie przypuszczam, że z tego wynikają potem błędy w wyliczeniach i wszystko się sypie.


Błędy wynikają z dokładności typu zmiennej, użyj funkcji sizeof(double) która zwróci ci ilość bajtów w których przechowywana jest zmienna. U mnie double ma rozmiar 8 bajtów. Nie trudno wydedukować, że kilka bitów będzie odpowiedzialne za rząd wielkości, a na reszcie zapisuje się po prostu liczbę. Czyli komputer pamięta liczbę 976563 którą potem pomnoży przez 10 do potęgi x, gdzie x tez pamięta. Możesz użyć typu long double wtedy powinna być większa dokładność. W większości przypadków po prosu wystarczy taka dokładność, bo 400 mln i 5 zł w zasadzie dla każdego jest równa 400mln zł :)




Użytkownicy przeglądający ten temat: 0

0 użytkowników, 0 gości, 0 anonimowych