Addition mit +=



  • Hallo ihr!

    Mich quält da ein Problem (nichts das man nicht lösen kann). Schaut mal:

    #include <iostream>
    
    int main(void)
    {
      double wt = -0.0000096;
      double ts = 0.002;
      double st = 0.0020096;
      wt += st - ts;
      std::cout << wt << std::endl;
      return 0;
    }
    

    Auf dem Papier ist wt = 0. Nichts aufregendes. Ich bekomme jedoch die Ausgabe: -1.999e-19 !

    Klar, man kann den Wert auch so berechnen:

    wt = wt + st - ts;
    

    Ich würde trotzdem gern wissen warum das schiefgeht und wie genau der Wert zustande kommt...

    Gruß
    😉



  • Ersetze die Zeile

    std::cout << wt << std::endl;
    

    einmal durch die Zeile

    std::cout << std::fixed << wt << std::endl;
    

    /edit: Vielleicht noch kurz zur Erklärung: Mit fixed erzwingst du die Ausgabe als Festpunktzahl und mit setprecision(n) setzt du die Anzahl der Dezimalstellen bei der Ausgabe.

    Und Vergleiche außerdem:

    #include <iostream>
    using namespace std;
    int main(void)
    {
      double wt = -0.0000096;
      double ts = 0.002;
      double st = 0.0020096;
    
      // noch ein test:
      cout << "\n wt - ts + st: " << fixed << setprecision(20) <<  wt - ts + st << endl;
      cout << " plain-calc:     " << -0.0000096 - 0.002 + 0.0020096 << endl;
    
      return 0;
    }
    

    Was merkst du? Richtig, der Prozessor rechnet irgendwie nicht korrekt. Weiß auch nicht, warum er es mit den Variablen nicht richtig kann, jedoch mit den plain-Werten. (Wahrscheinlich optimiert der Compiler bei den plain-werten geschickt).

    Mit dem +=-Operator hat das aber afaik nichts zu tun.

    Caipi



  • Caipi schrieb:

    Was merkst du? Richtig, der Compiler rechnet irgendwie nicht korrekt.

    Wieso der Compiler? Der Compiler übersetzt doch korrekt. Zur Ausführung (Lebenszeit der Variablen) wird er nicht mehr gebraucht.



  • nillable schrieb:

    Caipi schrieb:

    Was merkst du? Richtig, der Compiler rechnet irgendwie nicht korrekt.

    Wieso der Compiler? Der Compiler übersetzt doch korrekt. Zur Ausführung (Lebenszeit der Variablen) wird er nicht mehr gebraucht.

    Ja, du hast recht. Ich meine natürlich den Computer (insbesondere den Prozessor und für ganz genaue die ALU)).

    Und das trotz 4-maliger Bearbeitung 🙄

    Danke!

    Caipi



  • ebelcrom schrieb:

    wie genau der Wert zustande kommt...

    Weil float und double keine beliebig genauen Zahlen darstellen können.
    Einfach mal nach "double" in Verbindung mit "Genauigkeit" googeln.



  • Weil float und double keine beliebig genauen Zahlen darstellen können.

    Damit bin ich einverstanden, aber wir reden hier nicht von Rundungsfehlern die bei der "398sten" Nachkommastelle passieren... Die Zahlen und das Ergebnis sind klein genug um in double aufgenommen und korrekt berechnet zu werden!

    Zumindest hat es nichts mit dem += Operator zu tun. Also ich weiß nicht wie es euch geht, aber wenn mir der Rechner wt - ts + st zur Laufzeit nicht korrekt berechnen kann, dann ist das sehr erschreckend! 😞

    Gruß



  • dein computer kann nicht einmal 1/10 richtig ohne rundungsfehler rechnen



  • um der sache auf den Grundzugehen solltest du alle Werte einfach in eine Datei speichern, einen Hexeditor nehmen, dich über den verwendeten Gleitkommazahlstandard informieren und dann wirst du feststellen woran der fehler liegt



  • Ein Problem ist auch, dass manche endlichen Fliesskommazahlen des Dezimalsystems im Dualsystem nicht endlich dargestellt werden können. Man sollte deshalb darauf verzichten Endergebnisse mit so hoher Genauigkeit darzustellen. Bei vielen numerischen Algorithmen wird deshalb mit einem sogenannten "epsilon" gearbeitet. Epsilon ist ein kleiner Wert, den man als Ungenauigkeitsfehler erlaubt. Hier ein primitives Beispiel:

    #include <iostream>
    #include <cmath>
    
    double genauigkeit(double wert, double epsilon)
    {
        double ergebnis = static_cast<int>(wert);//alle nachkommastellen abmachen
        double schritt = .1;//mit zehntel genauigkeit anfangen
    
        while(fabs(wert-ergebnis)>epsilon)
        {
            while((wert-ergebnis)>epsilon)
                ergebnis+=schritt;
    
    		//wenns nicht genau genug ist,
    		//schritt zurück und schrittweite verfeinern
    		if(fabs(wert-ergebnis)>epsilon)
    		{
    			ergebnis-=schritt;
    			schritt/=10;
    		}
        }
        return ergebnis;
    }
    
    int main(void)
    {
      double wt = -0.0000096;
      double ts = 0.002;
      double st = 0.0020096;
      wt += st - ts;
    
      std::cout << genauigkeit(wt,1e-19) << std::endl; //nach der 19. Nachkommastelle abschneiden
      std::cout << genauigkeit(wt,1e-18) << std::endl; //nach der 18. Nachkommastelle abschneiden
      std::cin.get();
    }
    


  • Ein Problem ist auch, dass manche endlichen Fliesskommazahlen des Dezimalsystems im Dualsystem nicht endlich dargestellt werden können

    wie z. B. 1/10 😉

    falls du "nur" mit rationalen Zahlen genau rechnen willst schreib dir einfach ne Klasse Rational die dann genau rechnet - und nur bei Ausgaben aus dem Bruch einen double macht



  • #include <iostream>
    using namespace std;
    
    int main(void) 
    { 
    	int s = 0;
    	// c muss kleiner als 1 sein
    	double c = 0.002;
    	int s_max = 1000;
    
    	cout<<c<<" entspricht der Dualzahl"<<endl;
    	cout<<"0.";
    
    	while(!(c == 0) && !(s == s_max))
    	{
    		s = s  + 1;
    		double a = c*2;
    		cout<<int(a);
    		c = a - int(a);
    	}
    
    	if(s == 1000)
    		cout<<"konte nicht berechnet werden"<<endl;
    
    cout<<endl;
    
      system("pause");
    }
    

Anmelden zum Antworten