Ergebnis 1 bis 11 von 11

[C++] Wurzelberechnung Schleife wird nicht ausgeführt

  1. #1 Zitieren
    Ehrengarde Avatar von Dermitri
    Registriert seit
    Nov 2004
    Beiträge
    2.897
    Hallo Leute,
    wiedermal ein C++ Problem.
    Und zwar habe ich ein Programm geschreiben, das mit Hilfe der Intervallschachtelung die Wurzel einer Zahl berechnen soll. Folgendes:
    Code:
    #include <iostream>
    using namespace std;
    
    int main (int argc, char * const argv[]) {
    	int a, b, c;
    	double d, e;
    cout << "Zahl eingeben, aus der die wurzel gezogen werden soll: ";
    	cin >> a;
    	cout << "Geschätztes Intervall eingeben; von:  ";
    	cin >> b;
    	cout << " , bis: ";
    	cin >> c;
    	d=(b+c)/2;
    	e=d*d;
    	while (e!=a ) {
    		if (e<=a ) {
    			d=(d+c)/2;
    			e=d*d;
    		}
    		if (e>=a ) {
    			d=(d-c)/2;
    			e=d*d;
    		}
    	}
    	cout << "Die Wurzel aus " << a << "ist" << d ;
    }
    Mein Problem ist, dass die Schleife nicht ausgeführt wird. Es kommt kein Compiler-Fehler. Aber sobald ich b und c eingegebe kommt keine Reaktion.
    Findet jemand den Fehler?
    Dermitri ist offline

  2. #2 Zitieren

    Batmanistrator
    Avatar von Thoronador
    Registriert seit
    Jul 2005
    Ort
    Morrowind, Vvardenfell-Distrikt
    Beiträge
    20.799
    Zitat Zitat von Dermitri Beitrag anzeigen
    Mein Problem ist, dass die Schleife nicht ausgeführt wird.
    Ganz im Gegenteil, die Schleife wird nicht nur ausgeführt, sie läuft mit etwas Glück sogar endlos lange, da die Laufbedingung immer wahr ist. Was du als "keine Reaktion" ansiehst, ist einfach das endlos lange Laufen der Schleife. Zur Ausgabe der berechneten Wurzel kommt es dadurch nie.

    Zunächst ist zu sagen, dass bei Gleitkommazahlen das Prüfen auf Gleichheit oder Ungleichheit nicht wirklich immer sinnvoll ist, gerade in so einem Fall wie dem deinen, weil Gleitkommazahlen nur eine begrenzte Genauigkeit haben. Wenn du z.B. die Wurzel von 17 berechnen lassen willst (die Wurzel ist ca. 4,123106), ist nicht sichergestellt, dass der errechnete Wert nach dem Quadrieren auch wieder genau 17 ergibt. Quadriert man den Wert, könnte z.B. 17,000003 rauskommen, was aber ungleich 17 ist. Ergo läuft die Schleife weiter. Allerdings ist durch die begrenzte Darstellungsgenauigkeit von Gleitkommazahlen nicht sichergestellt, dass zu jeder Zahl auch eine Wurzel gefunden wird, die quadriert exakt die Ausgangszahl ergibt. Deswegen ist es besser, wenn man nicht auf Gleichheit (oder Ungleichheit) prüft, sondern fragt, ob die beiden Zahlen recht eng beieinander liegen (d.h. Betrag der Differenz beider Zahlen ist kleiner als beispielsweise 0,0001).

    Dann gibt es noch zwei weitere Probleme.
    Erstens: Du prüfst nicht, ob die eingegebenen Werte b und c so gewählt wurden, dass b<c ist, oder dass die Wurzel überhaupt in dem gewählten Intervall liegen kann! Bleiben wir bei der Wurzel aus 17. Das Intervall sollte so gewählt sein, dass (mindenstens) 4 und 5 darin liegen. Wenn jetzt aber jemand z.B. 12 und 18 als Intervallgrenzen eingibt, hast du ein Problem, denn damit wird man kein Ergebnis finden, und die Schleife läuft dann ebenfalls endlos lang.

    Zweitens: Der Teil in der Schleife ist nicht ganz richtig. Ich würde die Schleife so umschreiben, unter der Annahme, dass b<c und b und c als double und nicht als int deklariert wurden:
    Code:
    #include <cmath> //wird für fabs() benötigt
    
    ...
    
    while (fabs(e-a)>0.0001)
    {
      if (e<a)
      {
        b = d;
        d=(d+c)/2;
        e=d*d;
      }
      else if (e>a)
      {
        c = d;
        d=(b+d)/2;
        e=d*d;
      }
    }
    Thoronador ist offline

  3. #3 Zitieren
    Ritter Avatar von ojas
    Registriert seit
    Jun 2008
    Ort
    Erde
    Beiträge
    1.787
    Zitat Zitat von Thoronador Beitrag anzeigen
    ... weil Gleitkommazahlen nur eine begrenzte Genauigkeit haben.
    Ferner geht schon alleine beim Compilieren des Programm Genauigkeit verloren. Auch Zahlen mit kurzer Dezimalentwicklung könnnen nämlich vom Rechner nicht immer genau dargestellt werde. Da kann es auch schon mal passieren, das 0.5 * 0.5 != 0.25 ist.
    ojas ist offline

  4. #4 Zitieren

    Batmanistrator
    Avatar von Thoronador
    Registriert seit
    Jul 2005
    Ort
    Morrowind, Vvardenfell-Distrikt
    Beiträge
    20.799
    Zitat Zitat von ojas Beitrag anzeigen
    Auch Zahlen mit kurzer Dezimalentwicklung könnnen nämlich vom Rechner nicht immer genau dargestellt werde. Da kann es auch schon mal passieren, das 0.5 * 0.5 != 0.25 ist.
    Der erste, zitierte Satz stimmt zwar, wenn man z.B. 0.1 betrachtet, aber dein Beispiel ist doch etwas unglücklich gewählt, denn 0.5 und 0.25 sind im Zweiersystem mit so wenigen Mantissenbits exakt darstellbar, dass diese beiden Zahlen eigentlich von jedem PC intern exakt dargestellt werden können. Man braucht dazu ja nur ein Mantissenbit. (Bzw. sogar null Mantissenbits, weil das erste Bit der Mantisse bei IEEE 754 ausgelassen und implizit als 1 angenommen wird.)
    Thoronador ist offline

  5. #5 Zitieren
    Ritter Avatar von ojas
    Registriert seit
    Jun 2008
    Ort
    Erde
    Beiträge
    1.787
    Ja, nur ist nirgendwo festgeschrieben, das IEEE 754 verwendet werden muss. Auch die Verwendung des Binärsystems ist nirgendwo vorgeschrieben. Im Grunde genommen macht C++ überhaupt keine Annahmen darüber, wie Zahlen dargestellt werden. Genau das sollte durch das Beispiel verdeutlicht werden, deshalb ist es gut gewählt.

    In Java ist ein float ein 32-bit großer Wert nach IEEE 754. C++ legt das nicht so genau fest und Compiler nehmen meistens aus Performance-Gründen einfach das, was der Prozessor anbietet (also de fakto 32 Bit IEEE 754, nicht de jure).
    ojas ist offline Geändert von ojas (27.10.2010 um 12:25 Uhr)

  6. #6 Zitieren

    Batmanistrator
    Avatar von Thoronador
    Registriert seit
    Jul 2005
    Ort
    Morrowind, Vvardenfell-Distrikt
    Beiträge
    20.799
    Natürlich macht die Sprache an sich keine Vorschriften, wie die Zahlen dargestellt werden müssen, aber schau dir doch mal die heutigen Prozessoren an und du wirst sehen, dass da keiner seine Gleitkommarithmetik auf (beispielsweise) Basis 7 macht, sondern alle Basis 2 nehmen, weil das technisch am einfachsten zu handhaben ist. IEEE 754 ist auch nicht vorgeschrieben, aber trotzdem wird dieser Standard in der CPU/FPU umgesetzt, sodass man de fakto immer mindestens single und double precision floating-point format nach IEEE 754 zur Verfügung hat. (Ob das nun de jure oder nur de fakto ist, spielt doch keine Rolle mehr, wenn der Standard immer genutzt wird.)

    Aber wir kommen hier wohl etwas vom Thema und der eigentlichen Frage ab.
    Thoronador ist offline

  7. #7 Zitieren
    Ritter Avatar von ojas
    Registriert seit
    Jun 2008
    Ort
    Erde
    Beiträge
    1.787
    Zitat Zitat von Thoronador Beitrag anzeigen
    Ob das nun de jure oder nur de fakto ist, spielt doch keine Rolle mehr, wenn der Standard immer genutzt wird.
    Stimmt; zumindest solange sich daran nichts ändert. Andererseits: wer garantiert, das sich daran nichts ändert?

    Zitat Zitat von Thoronador Beitrag anzeigen
    Aber wir kommen hier wohl etwas vom Thema und der eigentlichen Frage ab.
    OK, zurück zum Thema: du hast im zweiten Beitrag die Gründe für das Verhalten des Programms richtig dargestellt. Ich habe dem nichts hinzuzufügen
    ojas ist offline

  8. #8 Zitieren
    Ehrengarde Avatar von Dermitri
    Registriert seit
    Nov 2004
    Beiträge
    2.897
    Vielen Dank für eure schnelle und ausführliche Hilfe
    Leider klappts immer noch nicht:
    Code:
    #include <cmath> //wird für fabs() benötigt
    using namespace std;
    
    int main (int argc, char * const argv[]) {
    	double a, b, c;
    	double d, e;
    cout << "Zahl eingeben, aus der die wurzel gezogen werden soll: ";
    	cin >> a;
    	cout << "Geschätztes Intervall eingeben: ";
    	cin >> b;
    	cout << "bis: ";
    	cin >> c;
    	d=(b+c)/2;
    	e=d*d;
    	while (fabs(e-a)>0.0001) {
    		if (e<=a ) {
    			d=(d+c)/2;
    			e=d*d;
    		}
    	else if (e>=a ) {
    			d=(d-b)/2;
    			e=d*d;
    		}
    	}
    	cout << "Die Wurzel aus " << a << " ist " << d ;
    }
    Ich glaube, das Programm kommt immer noch nicht aus der Schleife raus.
    Dermitri ist offline Geändert von Dermitri (27.10.2010 um 21:16 Uhr)

  9. #9 Zitieren

    Batmanistrator
    Avatar von Thoronador
    Registriert seit
    Jul 2005
    Ort
    Morrowind, Vvardenfell-Distrikt
    Beiträge
    20.799
    Zitat Zitat von Dermitri Beitrag anzeigen
    Leider klappts immer noch nicht
    [...]
    Ich glaube, das Programm kommt immer noch nicht aus der Schleife raus.
    Das ist auch nicht verwunderlich. Von den drei Problemen/Fehlern, die ich in meinem ersten Beitrag angesprochen habe, hast du nur einen einzigen behoben, und das reicht hier offenbar nicht.
    Thoronador ist offline

  10. #10 Zitieren
    Ehrengarde Avatar von Dermitri
    Registriert seit
    Nov 2004
    Beiträge
    2.897
    Öhm,
    also das b und c in der richtigen Reihenfolge eingegeben wird, davon gehe ich jetzt einfach mal aus, zumindestens sorge ich bei meiner Eingabe dafür. Hier soll es erstmal darum gehen, dass die Schleife unter "Idealvorraussetzungen" überhaupt berechnet wird.
    Außerdem habe ich alle Variablen mit double deklariert.
    Die if-Abfrage habe ich auch geändert. Das Resultat ist das gleiche
    Dermitri ist offline

  11. #11 Zitieren

    Batmanistrator
    Avatar von Thoronador
    Registriert seit
    Jul 2005
    Ort
    Morrowind, Vvardenfell-Distrikt
    Beiträge
    20.799
    Dann gehen wir der Einfachheit halber mal davon aus, dass die Eingabe "vernünftig" ist (d.h. b<c und die Lösung liegt auch wirklich im Intervall [b;c]), obwohl das nicht geprüft wird.

    Zitat Zitat von Dermitri Beitrag anzeigen
    Außerdem habe ich alle Variablen mit double deklariert.
    Die if-Abfrage habe ich auch geändert. Das Resultat ist das gleiche
    Trotzdem ist da noch mehr, was geändert wurde. Die Variablen b und c wurden ja deshalb extra umdeklariert, damit man ihnen im Laufe der Berechnungen Gleitkommawerte zuweisen kann. (Der Code wurde getestet und unter "Idealvoraussetzungen" wird die Schleife auch einige, wenige Male durchlaufen und dann das Ergebnis, welches recht nah an der Wurzel liegt, ausgeben.)
    Zitat Zitat von Thoronador Beitrag anzeigen
    Zweitens: Der Teil in der Schleife ist nicht ganz richtig. Ich würde die Schleife so umschreiben, unter der Annahme, dass b<c und b und c als double und nicht als int deklariert wurden:
    Code:
    #include <cmath> //wird für fabs() benötigt
    
    ...
    
    while (fabs(e-a)>0.0001)
    {
      if (e<a)
      {
        b = d;
        d=(d+c)/2;
        e=d*d;
      }
      else if (e>a)
      {
        c = d;
        d=(b+d)/2;
        e=d*d;
      }
    }
    Unterschiede zu deinem Code in der Schleife sind rot markiert.
    Thoronador ist offline

Berechtigungen

  • Neue Themen erstellen: Nein
  • Themen beantworten: Nein
  • Anhänge hochladen: Nein
  • Beiträge bearbeiten: Nein
  •