bei null kommt ein unerwartete zahl heraus. Unerwarteter Rundungsfehler trotz exakter zahlen



  • Hallo,
    ich sitzte schon seit einigen Stunden an einem größer Programm(jedenfals für mich). Es geht grob gesagt um die ausgabe eines Koordinatensystem in der Konsole und zusätzlich noch liniare funktionen in den Koordinatensystem. Ich bin gerade dabei den schnittpunkt einer linearen funktion der x-Achse zu ermittel.
    Der code wurde gekürzt und wurde für die Funtion f(x)= 0,5x+1 umgeschrieben.

    #include <iostream>
    
    using namespace std;
    int main(){
    
    double x = 10; //zehn ist der höchste  wert des koordiantenys.
    double xl = -x; //-zehn ist der niedrigste wert des koordiantenys.
    double m= 0.5;
    double b = 1;
    double schnitty;
    double ynull = 300;
    double xnull = 660;
    double fx2;
    
    	double xX = xl;
        cout << "m : " << m << endl;
    	cout <<"B " <<b << endl;
    
    	schnitty = b;
    	double schnittx;
        cout << "Schnittpunkt ykoordinate " << schnitty << endl;
    	cout << ynull << " " << xnull << endl;
    	while (true) {
    
    		fx2 =(m *xl) + b; // steht für f(x)=mx+b
    
    		if (fx2 == 0 ) 
     /*((fx2 > -0.0001) &&(fx2 <-0.00000000000000000000000000000001)) hiermit würde es funktionieren*/        
    
    {  
    			schnittx = xl;
    			cout << "Schnittpunkt Xkoordinate " << schnittx << endl;
    		}
    
    		if (((ynull - (fx2 * 25)) + 0.5) < 50) {
    			break;
    		}
    
    		xX += 0.01;
    		xl = xX;
    
    	}
    	}
    

    das problem ist das bei f(x) = 0,5*-2 +1 nicht null sonder irgendeine kommazahl rauskommt.



  • win8789 schrieb:

    Hallo,

    [...]
    		if ((fx2 =0 ) 
     /*((fx2 > -0.0001) &&(fx2 <-0.00000000000000000000000000000001)) hiermit würde es funktionieren*/        
    [...]
    

    das problem ist das bei f(x) = 0,5*-2 +1 nicht null sonder irgendeine kommazahl rauskommt.

    Du muss den Operator == verwenden. Du machst eine Zuweisung.



  • Onkki schrieb:

    Du muss den Operator == verwenden. Du machst eine Zuweisung.

    oh der fehler muss wohl beim abtippen und kopieren geschehen sein. im orignal ist er jedenfalls nicht vorhaden. Es funktioniert also selbst mit == nicht.



  • Der Code kompiliert so gar nicht.



  • Minimalbeispiel schrieb:

    Der Code kompiliert so gar nicht.

    noch ein abtip fehler bei if ist eine klammer zu viel. jetzt sollte es aber compilierbar sein.



  • Hallöchen,

    ich möchte ein paar Anmerkungen machen:
    - Lies dir bitte diesen Thread hier gründlich durch: https://www.c-plusplus.net/forum/200753.

    das problem ist das bei f(x) = 0,5*-2 +1 nicht null sonder irgendeine kommazahl rauskommt.

    Das ist leider in keinster Weise eine hilfreiche Problembeschreibung. Desweiteren solltest du zumindest dafür sorgen, dass dein geposteter Code kompiliert und ordentlich formatiert ist.

    Deine Variablenamen sind nicht immer sehr aussagekräftig. Einzelne Buchstaben sind fast immer schlecht. Es ist ebenso schlechter Stil, uninitialisierte Variablen zu verwenden. Du solltest sie in C++ erst definieren, wenn du auch einen sinnvollen Wert dafür hast.

    Falls du es noch nicht wusstest, 'endl' ist mehr als nur ein Zeilenumbruch ('\n'). Dies ist in diesem Fall vermutlich nicht weiter ausschlaggebend, doch sollte man sich dessen bewusst sein.

    Meine Vermutung zu deinem lückenhaft beschriebenen Problem ist, dass gewisse Rundungsfehler/Darstellungsfehler entstehen können, durch die Verwendung von Fließkommazahl-Datentypen (also halt double 'undso). Operationen wie der exakte Vergleich mit 0 laufen dadurch schief (da eben doch nicht exakt 0). Dein Ansatz, d.h. prüfen ob eine Zahl fast Null ist, scheint mir akzeptabel zu sein. Ich erinnere mich noch daran, dass in der Schule Ergebnisse, die im GTR mit einer Potenz von z.B. 10^-14 standen, ebenfalls als exakt 0 gewertet wurden (z.B. bei der Ermittlung von Nullstellen).

    Man könnte z.B. so eine Funktion verwenden, damit es etwas übersichtlicher bleibt:

    bool is_almost_zero(double val) {
        return val > -10e-10 && val < 10e-10;
    }
    

    Ich habe deinen Code damit getestet und erhielt y = 1 und x = -2 (ich denke das ist so gewollt?). Aber im Prinzip habe ich nichts anderes getan, als was du schon kommentiert hast:

    /*((fx2 > -0.0001) &&(fx2 <-0.00000000000000000000000000000001)) hiermit würde es funktionieren*/
    

    Vielleicht erscheint dir meine Erklärung verständlich (ich hoffe sie ist richtig, sonst korrigiert mich bitte!).

    LG
    HarteWare

    P.S.
    Ich würde dir empfehlen, ein gutes Buch für C++ zu besorgen (soll jetzt nicht böse gemeint sein. Ich bin selbst auch nicht gerade C++ Profi!).



  • HarteWare schrieb:

    Hallöchen,

    ich möchte ein paar Anmerkungen machen:
    - Lies dir bitte diesen Thread hier gründlich durch: https://www.c-plusplus.net/forum/200753.

    das problem ist das bei f(x) = 0,5*-2 +1 nicht null sonder irgendeine kommazahl rauskommt.

    Das ist leider in keinster Weise eine hilfreiche Problembeschreibung. Desweiteren solltest du zumindest dafür sorgen, dass dein geposteter Code kompiliert und ordentlich formatiert ist.

    Deine Variablenamen sind nicht immer sehr aussagekräftig. Einzelne Buchstaben sind fast immer schlecht. Es ist ebenso schlechter Stil, uninitialisierte Variablen zu verwenden. Du solltest sie in C++ erst definieren, wenn du auch einen sinnvollen Wert dafür hast.

    Falls du es noch nicht wusstest, 'endl' ist mehr als nur ein Zeilenumbruch ('\n'). Dies ist in diesem Fall vermutlich nicht weiter ausschlaggebend, doch sollte man sich dessen bewusst sein.

    LG
    HarteWare

    P.S.
    Ich würde dir empfehlen, ein gutes Buch für C++ zu besorgen (soll jetzt nicht böse gemeint sein. Ich bin selbst auch nicht gerade C++ Profi!).

    Als erstes mal danke für die Hilfe.

    Als zeites. Ja ich hab den Thread schon vorher gelesen(sonnst hätte ich wohl die gut 1000 restlichen zeilen(das ist kein scherz) auch noch eingefügt ;).
    Aber du hast recht, dass ich mir besser Namen für die Variablen nehmen soll und eine längere erklärung.

    drittens mir ist dir unterschied zwischen endl und \n bewusst.

    viertnes: Es funktioniert leider nicht perfekt. wenn ich zum beispiel sage, dass die Steigung m = 3 ist und b (wird auch als n bezeichnet) = 7 ist kommt kein ergbnis vom schnittpunkt der x-achse raus.

    Des weitern würde es mich interessieren warum bei

    fx2 =(m *xl) + b;
    
    //wenn m = 0,5 ist, xl = -2, und b 2(wie im amfänglischen beispiel
    fx2 =(0,5 *-2) + 1;
    

    nicht fx2 = 0 rauskommt und der Schnittpunkt dementsprächen -2 ist.

    ps. kannst du mir ein buch empfelen. Ich arbeite momentan "c++ einführung und professionelle programmierung" von Ulrich brymann durch. In der schule muss ich mich hingegen mit java herumschlagen(meine lehrer können kein c++)



  • win8789 schrieb:

    Des weitern würde es mich interessieren warum bei

    fx2 =(m *xl) + b;
    
    //wenn m = 0,5 ist, xl = -2, und b 2(wie im amfänglischen beispiel
    fx2 =(0,5 *-2) + 1;
    

    nicht fx2 = 0 rauskommt und der Schnittpunkt dementsprächen -2 ist.

    HarteWare hat bereits die Rundungsfehler bei Berechnungen Fließkommazahlen ( double ) erwähnt und is_almost_zero als akzeptable Lösung vorgeschlagen. Dem schließe ich mich an.
    Jede Rechenoperation auf Fließkommazahlen kann Rundungsfehler in das Ergebnis einbringen, selbst so scheinbar harmlose Berechnungen wie du oben aufgeführt hast.
    In solchen Fällen prüft man auch z.B. in wissenschaftlicher Software, wo man möglichst genaue Ergebnisse benötigt auf "fast Null". Das ist also kein "Hack" weil man es nicht besser weiss.
    Wenn man es extrem genau benötigt, kann man sich auch eine präzise Obergrenze für den möglichen Rundungsfehler ermitteln und so den Toleranzbereich für "fast Null" minimieren.
    Das wird allerdings je nach durchgeführten Berechnungen schnell recht kompliziert und diese Obergrenze gilt dann auch nur für die eine spezielle Berechnung (muss also jedesmal angepasst werden).
    Ein Pi mal Daumen-Wert wie in HarteWares is_almost_zero ist also auf jeden Fall zu bevorzugen, wenn man es nicht unbedingt ganz genau benötigt.

    Finnegan



  • Anmerkung:

    wenn man genau die von dir genannten Werte einstellt kommt exakt 0.0 raus und du findest den Schnittpunkt auch

    double x = 2;
    double xl = -x;
    double m= 0.5;
    double b = 1;
    

    in anderern Fällen sind es wohl Rundungsfehler bei Berechnungen Fließkommazahlen

    http://stackoverflow.com/questions/17333/most-effective-way-for-float-and-double-comparison



  • Nochmals hallo,

    es kann sein, dass 10e-10 nicht ausreicht, um bestimmte Ungenauigkeiten abzudecken. Eventuell hast du mehr Erfolg, wenn du 10e-7, oder 10e-4 verwendest. Ich denke, dass die Genauigkeit in dem Bereich trotzdem noch ausreicht, kann aber nichts garantieren.

    Zum Thema Bücher: Ich habe dein genanntes Buch nicht gelesen und kann daher leider keine Auskunft dazu geben.
    Ein paar Links zu Büchern:
    https://www.c-plusplus.net/forum/310212 (insbesondere "C/C++ Forum :: C++ Lernen - passende Lektüre und richtiger Anfang")
    https://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list

    Wenn man sich auf deutsche Bücher beschränkt, ist die Auswahl nicht ganz so groß.

    Ich will dich jetzt in keinster Weise dazu bringen, ein weiteres C++ Buch zu kaufen, oder sagen, dass dein jetztiges Buch schlecht ist. Mir sind eben nur kleine Dinge aufgefallen, wie z.B. uninitialisierte Variablen, und dass der Großteil der Variablen am Anfang des Programs definiert wurde, so wie es vielleicht in einem C++ online Tutorial gezeigt werden würde, welches eigentlich ein C mit cout Tutorial ist.

    Auf den "is_almost_zero" Ansatz ist der TE eigentlich selbst gekommen, ich habe dem Ganzen lediglich einen Namen verpasst 😃

    LG
    HarteWare



  • HarteWare schrieb:

    Nochmals hallo,

    Ich will dich jetzt in keinster Weise dazu bringen, ein weiteres C++ Buch zu kaufen, oder sagen, dass dein jetztiges Buch schlecht ist. Mir sind eben nur kleine Dinge aufgefallen, wie z.B. uninitialisierte Variablen, und dass der Großteil der Variablen am Anfang des Programs definiert wurde, so wie es vielleicht in einem C++ online Tutorial gezeigt werden würde, welches eigentlich ein C mit cout Tutorial ist.

    Hierzu muss ich zur meiner Verteidigung sagen, dass alle Varablen im Orignal vom User duch eingabe verstegellt werden oder sich aus der eingabe Ergeben. So ist ynull z.B die eingabe der (y-Achse*25 *50). 25 ist hierbei der abstand zwischen die eimzelnen zahlen im Koordiantensystem in Pixeln und 50 ist der abstand zum oberen Rand in pixeln.

    Außerdem hab ich mitlerweile das Programm nochmal mit der Header iomanip
    und scientific durchlaufen lassen um eine wissenschaftliche schreibweise der Ergebnisse zu bekommen.

    m: 5.000000e-001
    b: 1.00000e+000
    xl: -2.000000e+000
    
    das ergibt dann:
    fx: -3.108624e-015
    

    Aber warum immerhin sind die anderen zahlen alle exakt.



  • Wahrscheinlich ist der rundungsfehler im Ergebnis so groß, als dass dieser durch die 'automatische fehlerbehebung' beim ausgeben ausgeglichen wird.



  • roflo schrieb:

    Wahrscheinlich ist der rundungsfehler im Ergebnis so groß, als dass dieser durch die 'automatische fehlerbehebung' beim ausgeben ausgeglichen wird.

    kann ich irgendie die 'automatische Fehlerbehebung' umgehen. Damit ich mir sicher sein kann, dass alles richtig angezeigt wird.
    Monentan sind die Varablen.

    double x = 2.3; 
    double xl = -x; 
    double m= 0.500;
    double b = 1.000;
    

  • Mod

    win8789 schrieb:

    Aber warum immerhin sind die anderen zahlen alle exakt.

    Sind sie wahrscheinlich nicht.
    http://ideone.com/rugtNM
    Wären sie exakt, so müsste auch das Ergebnis exakt sein. Da nur 2er-Potenzen in allen Variablen und Zwischenergebnissen beteiligt und diese alle exakt darstellbar sind.



  • camper schrieb:

    Wären sie exakt, so müsste auch das Ergebnis exakt sein. Da nur 2er-Potenzen in allen Variablen und Zwischenergebnissen beteiligt und diese alle exakt darstellbar sind.

    Ok du hast recht. Ich habe nun nochmal etwas nachgedacht, allerdings konnte ich keine möglichkeit finden es zu einhundert prozent genau zu machen 😕

    allerdings konnte ich mit

    (fx2 > -0.009) &&(fx2 <-0.0000000000000000000000000000000000000000000000000001)
    

    dafür sorgen, dass es fast immer funktioniert, wenn auch manchmel zwei oder drei ergnisse, welche fast identisch sind, rauskommen.

    Danke nachmal. Hab jetzt etwas mehr gelernt.


Log in to reply