• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

Błąd przy odejmowaniu liczb

Object Storage Arubacloud
+1 głos
164 wizyt
pytanie zadane 26 grudnia 2015 w C i C++ przez panajot94 Nowicjusz (150 p.)
edycja 26 grudnia 2015 przez panajot94

Witam, po uruchomieniu tak skonstruowanego programu, powinienem uzyskać wynik -2, jednak dostaje wynik -2-1.11022e-16i, i nie wiem czym jest to spowodowane. Dodam, że zmieniając w klasie double re, im; na float re, im; program działa prawidłowo. Może ktoś wie na czym polega błąd?

 

class LiczbaZespolona{
public:
    LiczbaZespolona operator * (const LiczbaZespolona &L);
    LiczbaZespolona(double _re, double _im);
private:

    double re, im;
    friend ostream & operator << (ostream &str, const LiczbaZespolona &L);
};

LiczbaZespolona LiczbaZespolona::operator * (const LiczbaZespolona &L){
    return LiczbaZespolona(re*L.re-im*L.im,im*L.re+re*L.im);
}

LiczbaZespolona::LiczbaZespolona(double _re, double _im): re(_re), im(_im)
{}

int main(){
    LiczbaZespolona liczba_1(3, -1), liczba_2(-0.6,-0.2);
    LiczbaZespolona wynik = liczba_1 * liczba_2;

    cout << wynik << endl;

    return 0;
}

ostream & operator << (ostream &str, const LiczbaZespolona &L){
    if(L.im == 0) str << L.re;
    if((L.re == 0) && (L.im != 0) && (L.im != 1)) str << L.im << "i";
    if((L.re != 0 ) && (L.im > 0) && (L.im != 1)) str << L.re << "+" << L.im << "i";
    if((L.re != 0) && (L.im < 0) && (L.im != 1)) str << L.re << L.im << "i";
    if((L.re == 0) && (L.im != 0) && (L.im == 1)) str << "i";
    if((L.re != 0 ) && (L.im > 0) && (L.im == 1)) str << L.re << "+" << "i";
    if((L.re != 0) && (L.im < 0) && (L.im == 1)) str << L.re << "i";

    return str;
}

 

1 odpowiedź

+2 głosów
odpowiedź 26 grudnia 2015 przez Krawiec91 Pasjonat (19,600 p.)

Zmień notację naukową na zwykłą z przecinkiem w definicji przeciążenia operatora <<.

    str.precision(2);
    str.setf(std::ios::fixed);
komentarz 26 grudnia 2015 przez panajot94 Nowicjusz (150 p.)
Dzięki za pomoc w rozwiązaniu problemu.

A wie może ktoś, co jest przyczyną tego problemu?
komentarz 26 grudnia 2015 przez Krawiec91 Pasjonat (19,600 p.)
Typy zmiennoprzecinkowe (float, double, long double ) mają określoną dokładność i niekiedy przy określonej liczbie bitów nie jest możliwe przechowywanie dokładnej wartości. To co jest przechowywane obarczone jest pewnym błędem wynikającym z ilości bitów do wykorzystania na zmienną.
Pozmieniaj sobie w kodzie ilość wyświetlanych miejsc po przecinku w metodzie precision() i zobacz jak wygląda sytuacja.
Równie dobrze rozpisz sobie na kartce jak wygląda dokładność liczb 0.6 przy różnej liczbie bitów do zapisania (2^-1 + 2^-2 + 2^-3 + 2^4 + ..... 2^-n). O ile liczbie 0.5 bez problemu w postaci jednego bitu  przedstawisz 0.1 (2^-1) to w przypadku liczby 0.6 zobacz jak sytuacja się komplikuje, i ile bitów jest potrzebne, żeby to zapisać z określoną dokładnością.;)
komentarz 26 grudnia 2015 przez panajot94 Nowicjusz (150 p.)

Zgodzę się z Tobą, ale w tym przypadku chyba chodzi o coś innego. Bo jak wytłumaczyć fakt, że dla float program działa prawidłowo, gdzie float ma dwa razy mniejszą dokładność niż double. Dodatkowo zrobiłem coś takiego

LiczbaZespolona LiczbaZespolona::operator * (const LiczbaZespolona &L){
{
    LiczbaZespolona tmp;

    tmp.real = re*L.re-im*L.im;
    tmp.imaginary = im*L.re+re*L.im;

//	TEST
    double zm1 = im*L.re;	//wyswietlajac da nam 0.6
    double zm2 = re*L.im;	//wyswietlajac da nam -0.6
    cout << zm1+zm2 << endl;	//po dodaniu mamy -1.11022e-16

    return tmp;
}

Wyświetlając zm1 i zm2, mamy odpowiednio 0.6 i -0.6, ale po dodaniu nie otrzymujemy zera. Również w tym przypadku jeśli zm1, zm2 ustawiam na typ float, to suma ich daje 0.

komentarz 26 grudnia 2015 przez Krawiec91 Pasjonat (19,600 p.)
Sprawdzałem ten poprzedni kod u siebie, zarówno przy zmiennych typu double jak i typu float ten "ogon" występował.
Z tym, że float wydaje się "dokładniejszy", to raczej kwestia zaokrągleń przy wyświetlaniu na ekran.
Na bazie programu, który wrzuciłeś w pierwszym poście, przy notacji zwykłej (fixed):
- zmienna float - wynik (cześć urojona liczby zespolonej)  różny jest od zera na 8 miejscu po przecinku,
- zmienna double - wynik różny jest od zera na 16 miejscu po przecinku.
Liczba miejsc znaczących przy zmiennej float wynosi 6, przy zmiennej double 15.
http://www.cplusplus.com/reference/cfloat/
Więc, że tak powiem składa się to do kupy.

Podobne pytania

+1 głos
1 odpowiedź 146 wizyt
pytanie zadane 18 grudnia 2015 w C i C++ przez Plessio Obywatel (1,090 p.)
+1 głos
2 odpowiedzi 158 wizyt
pytanie zadane 16 grudnia 2015 w C i C++ przez Plessio Obywatel (1,090 p.)
+2 głosów
2 odpowiedzi 176 wizyt

92,579 zapytań

141,431 odpowiedzi

319,657 komentarzy

61,963 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...