# [c++]quadratische gleichungen fehlersuche



## DarkSean (5. Februar 2006)

Hi,ich hab folgendes Problem.
Ich hab ein Programm geschrieben um quadratische Gleichungen zu lösen. Es funktioniert im Prinzip so wie es soll. Es löst jede Gleichung richtig und zeigt auch die Diskriminante an. Doch gebe ich die Gleichung 5x² + 14x+ 9,8 = 0 ein, zeigt das Prog etwas völlig falsches an.  Die Diskriminante ist bei der Gleichung 0, doch es wird -1,0... angezeigt. Ich weiß nicht ob bei anderen Gleichungen der gleiche Fehler auftritt, denn bis jetzt klappte es nur bei der Gleichung nicht. Hat jemand eine Lösung für mein Problem? Ich bin auch schon mein Code durchgegangen aber ich kann meinerseits keinen Fehler entdecken.

```
#include <iostream>
#include <conio.h>
#include <cmath>

using namespace std;

long double a,a1,p,p1,q,q1,d,x1,x2;

int main()
{
    while (bool i = 1)
    {
	cout << "Programm zur L\x94sung quadratischer Gleichungen der Form"
		 << endl << "a x ^ 2 + b x + c = 0";
	cout << endl
		 << endl
		 << "© 2006 Oliver Sabiniarz"	 
		 << endl
		 << endl << " Geben sie a ein: "
		 << endl << " a = ";
	cin  >> a;//5
	cout << endl
		 << endl << " Geben sie b ein: "
		 << endl << " b = ";
	cin  >> p;//14
	cout << endl
		 << endl << " Geben sie c ein: "
		 << endl << " c = ";
	cin  >> q;//9.8
	a1 = a;//a1=5
	a = a / a1;//a=1
	p1 = p / a1;//p1=2.8
	q1 = q / a1;//q1=1.96
	d = pow((p1 / 2), 2)-q1;//d=0
	if (a1 != 1)//true
	{
	cout 
		 << endl << a1 << " x ^ 2 + " << p << " x + " << q << " = 0  <=>  " << "x ^ 2 + " << p1 << " x + " << q1 << " = 0";//Ausgabe: 5x^2+14x+9.8=0<=>x^2+2.8+1.96=0
	}
	cout << endl <<"Diskriminante = " << d << endl;//Diskriminante = 0
	if (d > 0)
	{
		x1 = -p1 / 2 - sqrt(d);
		x2 = -p1 / 2 + sqrt(d);
		cout << endl <<"x1 = " << x1
	         << endl <<"x2 = " << x2;
	}	
	else if (d==0)
	{	
	    x2 = -p1 / 2 + sqrt(d);
	    cout << endl << "x = " << x2;
	}
	else if (d < 0)
	{
		d=-d;
        cout << endl <<"x1 = " << -p1 / 2 << " + " << sqrt(d) << " i"
             << endl <<"x2 = " << -p1 / 2 << " - " << sqrt(d) << " i";
	}
	cout << endl << endl << "Auf Wiedersehen!" << endl << endl << endl;
	getch();	
	}	
}
```


----------



## deepthroat (5. Februar 2006)

Hi.

Ich hab dein Programm mal ausprobiert und es wurde als Diskriminante -1.42139e-016 == -1.42139 * 10 ^ -16  == -0.000000000000000142139 berechnet.

Also ein Wert der "fast" 0 ist, aber eben nur fast.

Gleitkommazahlen (double, float) sind immer ungenau. Manche "einfache" gebrochen rationale Zahlen lassen sich mitunter nicht genau als Gleitkommazahl speichern. D.h. wenn du mit Gleitkommazahlen rechnest kannst du nicht erwarten ein genaues Ergebnis zu bekommen. Desweiteren ist es somit eine relativ schlechte Idee bei Gleitkommazahlen auf Gleichheit (z.B. == 0) zu prüfen.

Was man machen kann ist zu prüfen ob eine Gleitommazahl eben "fast" gleich einer anderen ist oder man verwendet eine Bibliothek die mit beliebiger Genauigkeit rechnet.

Gruß


----------



## DarkSean (5. Februar 2006)

Na ja, aber probier mal den Fall mit der oben angegebenen Gleichung, da kommt nämlich genau 0 raus, und trotzdem wird ein falscher wert ausgegeben.


----------



## Tobias K. (5. Februar 2006)

moin




> Na ja, aber probier mal den Fall mit der oben angegebenen Gleichung, da kommt nämlich genau 0 raus, und trotzdem wird ein falscher wert ausgegeben.


Genau das hat er gemacht! Und er hat ganz genau beschrieben warum der Fehler so auftritt!

Gebe mal ein "(int)" in die Ausgabe ein, also so::

```
cout << endl <<"Diskriminante = " << (int)d << endl;//Diskriminante = 0
```

So wird diese Ungenauigkeit "abgeschnitten".


mfg
umbrasaxum


----------



## DarkSean (5. Februar 2006)

Wenn ich die Diskriminante als Integer speicher, wird das Ergebnis meist ungenauer (unbrauchbar), und das will ich auch nicht. Was kann ich denn machen damit die Genauigkeit erhalten wird, aber solche Fehler nicht auftreten, außer auf ganze Zahlen zu runden? Wie geht das denn mit dem ungefähr 0? MIr wär das mit der "Bibliothek" noch lieber, nur habe ich damit keine Erfahrung. Wie geht das?
Mir ist gerade auch bei VB aufgefallen, das wenn ich 100 mit 99.99 subtrahiere kommt auch nicht 0.01 raus. Es wäre mir lieber gewesen, dass das ein Programmierfehler meinerseits gewesen wäre


----------



## deepthroat (5. Februar 2006)

DarkSean hat gesagt.:
			
		

> Wenn ich die Diskriminante als Integer speicher, wird das Ergebnis meist ungenauer (unbrauchbar), und das will ich auch nicht. Was kann ich denn machen damit die Genauigkeit erhalten wird, aber solche Fehler nicht auftreten, außer auf ganze Zahlen zu runden?


Das geht nicht mit den Gleitkommatypen von C++. Das Problem hier ist das schon 9,8 als long double nicht genau gespeichert werden kann. 
	
	
	



```
cout.precision(20); cout << (9.8l) << endl;
```
Wenn man mit einer solchen ungenau gespeicherten Zahl rechnet, setzt sich der Fehler der in dieser Zahl steckt fort und vervielfacht sich dementsprechend (je nach Operation).


			
				DarkSean hat gesagt.:
			
		

> Wie geht das denn mit dem ungefähr 0?


Naja, du brauchst nur zu schauen ob der Absolutwert kleiner als eine ganz kleine Zahl ist. Es gibt einen Wert Epsilon, der angibt wie genau man eine Zahl als Gleitkommazahl speichern kann.
	
	
	



```
#include <limits>

cout << numeric_limits<long double>::epsilon() << endl;
```



			
				DarkSean hat gesagt.:
			
		

> MIr wär das mit der "Bibliothek" noch lieber, nur habe ich damit keine Erfahrung. Wie geht das?


Vor ein paar Tagen war schonmal ein solches Thema hier im Forum, da ging es um die genaue Berechnung von Pi bis auf einige hundert Stellen genau. Da wurden einige Vorschläge zu Bibliotheken gemacht - MAPM (http://www.tc.umn.edu/~ringx004/mapm-main.html) sollte dafür ganz gut geeignet sein.

Gruß


----------



## DarkSean (5. Februar 2006)

Das gefäält mir schonmal, nur ich bin ein ziemlicher Anfänger im Programmieren und habe wirklich KEINEN Schimmer wie ich das einbauen soll. Wäre nett wenn mir einer eine Schritt-für-Schritt-Beschreibung geben könnte. Ich beherrsche Englisch zwar ziemlich gut, aber um die Seite zu verstehen reicht es dann doch wieder nicht.
Euer recht verzweifelter
Sean


----------



## deepthroat (6. Februar 2006)

Also, als erstes solltest du dir mal die mapm-492.zip Datei runterladen und entpacken. Da gibt's dann eine README Datei - die sollte man lesen. Da steht drin 





> DOS / Win NT/9x  (in a DOS window for NT/9x):
> 
> see the file 'README.DOS' for instructions.


In der README.DOS steht dann drin 





> ========================
> Microsoft Visual C++ 6.0
> ========================
> run : mkallmsc.bat             (builds library + 4 executables)


(Du mußt vorher evtl. VCVARS32.BAT im VC++ \bin Verzeichnis ausführen)

Damit du die Bibliothek dann in deinem Programm verwenden kannst mußt du einerseits die Header Dateien inkludieren wie es in den Beispielprogrammen die dabei sind vorgemacht ist (dafür mußt du auch das Verzeichnis wo die Header Dateien liegen irgendwo unter Projekt->Einstellungen: C/C++ hinzufügen) und andererseits die Bibliothek selbst unter Projekt->Einstellungen: Linker hinzufügen.

Da ich kein VC++ 6.0 habe kann ich dir das leider nicht genau sagen wie die Einstellungen heißen. Da mußt du einfach nochmal in der Hilfe nachschauen.

Gruß


----------



## DarkSean (6. Februar 2006)

Ich hab aber kein VC++ 6.0 sondern Dev-C++. Wie geht das denn da?


----------



## deepthroat (6. Februar 2006)

Achso, das ich ich dann wohl verwechselt. Ich war irgendwie davon überzeugt das du VC++ 6.0 geschrieben hattest - war aber wohl ein anderes Thema 

Mit Dev-Cpp kann ich dir besser helfen. Als erstes lädst du dir diesmal die Datei für Unix Systeme runter. (http://www.tc.umn.edu/~ringx004/mapm-4.9.2.tar.gz) und entpackst die (z.B. mit 7-zip oder einem anderen fähigen Entpackprogramm)

Ich werde mal annehmen das du Dev-C++ im Standardpfad c:\dev-cpp installiert hast - wenn nicht mußt du das folgende jeweils für deinen Pfad anpassen. 

Dann öffnest du eine DOS-Box und setzt erstmal den Pfad damit die Tools von Dev-C++ gefunden werden:
	
	
	



```
set PATH=%PATH%;c:\dev-cpp\bin
```
Dann wechselst du in das Verzeichnis wo du MAPM ausgepackt hast und rufst das Programm make folgendermaßen auf:
	
	
	



```
cd c:\build\mapm_4.9.2
make -f makefile.unx
```
Das sollte dann problemlos durchlaufen und eine Bibliothek "libmapm.a" und ein paar Beispielprogramme erstellen.

Um die Bibliothek zu verwenden mußt du sie nur unter Projekt->Optionen: Parameter: "Bibliothek/Objekt hinzufügen" -> libmapm.a Datei auswählen hinzufügen.

Außerdem mußt du wie gesagt noch die Header Dateien einbinden. Dazu kannst du entweder die .h Dateien vom MAPM Verzeichnis in dein Projektverzeichnis kopieren, oder du fügst das MAPM Verzeichnis unter Projekt->Optionen: Verzeichnisse: Include Verzeichnisse hinzu.

Gruß


----------



## DarkSean (6. Februar 2006)

Ich glaub ich gib's auf. Ich hab alles so gemacht wie du es gesagt hast, aber mein DOS-Fenster streikt .
Hier Screenshot:


----------



## deepthroat (6. Februar 2006)

Hihi. Um das Laufwerk zu wechseln mußt du nur den Laufwerksbuchstaben mit Doppelpunkt eintippen. Das cd funktionert schon, nur getrennt nach Laufwerk, d.h. du hast auf dem Laufwerk e: das Verzeichnis gewechselt, befindest dich aber auf Laufwerk c: Also daran hatte ich wirklich nicht gedacht... 


```
<Laufwerk>:
cd \
cd mapm_4.9.2
make -f makefile.unx
```
So sollte es jedenfalls funktionieren.

Gruß


----------



## DarkSean (11. Februar 2006)

muss ich denn noch irgendeine spezielle headerdatei in meinen code einbiden, denn bis jetzt zegt mein programm das gleiche ergebnis wie zuvor an.


----------



## deepthroat (11. Februar 2006)

Ja, sicher mußt du das.

Außerdem kannst du natürlich nicht mehr die Standard-Typen benutzen sondern die MAPM Klasse.

Du hättest ja auch ruhig mal die Beispielprogramme anschauen können die schon dabei sind.

Hier nochmal ein kleines Beispiel:

```
#include <cstdlib>
#include <iostream>
#include <locale>

#include <m_apm.h>

using namespace std;

ostream& operator<< (ostream& out, const MAPM& mapm_var) {
    const int digits = mapm_var.significant_digits();

    char* buffer = new char[digits + 12];
    
    if (out.flags() & ios::fixed) {
      mapm_var.toFixPtString(buffer, digits);
    } else {
      mapm_var.toString(buffer, digits);
    }
    out << buffer;

    delete[] buffer;
    
    return out;
}

istream& operator>> (istream& in, MAPM& mapm_var) {
    // parse a floating point or integer number, if successful
    // assign it to mapm_var.
    // 
    // note: the parsing is somewhat redundant since operator= 
    //       of the MAPM class just calls the mapm_set_string
    //       function which parses the string AGAIN into a
    //       number, but since the function just returns void and
    //       hence does not indicate an error (except printing an
    //       error message on the console) this seems necessary.

    enum  { start,     /* initial state / skip whitespace */
            sign,      /* seen a minus / plus sign */
            integer,   /* read an integer value */
            point,     /* encountered a decimal point */
            floatnum,  /* read a floating point number */
            floateE,   /* floating point number with [eE] suffix */
            floatexp } /* floating point number with exponent and optional plus/minus sign */
          state = start;
    int c;
    string num; 

    const char decimal_point = use_facet<numpunct<char> >(in.getloc()).decimal_point();

    while(in && (c = in.get()) != EOF) {
      if (isspace(c)) {
         if (state == start) continue;
         else break;
      } else if (isdigit(c)) {
         switch (state) {
         case start:                      /* fall through */
         case sign:     state = integer;  /* fall through */
         case integer:  break;

         case point:    state = floatnum; /* fall through */
         case floatnum: break;

         case floateE:  state = floatexp; /* fall through */
         case floatexp: break;
         }
      } else if (c == decimal_point) {
         bool exit_loop = false;

         switch (state) {
         case start:
         case sign:     state = point; break;

         case integer:  state = floatnum; break;

         default:
             exit_loop = true;
         }
         if (exit_loop) break;
      } else if (c == '+' || c == '-') {
         if (state == start) state = sign;
         else if (state == floateE) state = floatexp;
         else break;
      } else if (c == 'e' || c == 'E') {
         if (state == integer || state == floatnum) state = floateE;
         else break;
      } else {
         break;
      }
      num += static_cast<char>(c);
    }
    switch (state) {
    /* accept states. */
    case integer:
    case floatnum:
    case floateE:
    case floatexp:
         mapm_var = num.c_str(); break;
    default:
         in.setstate (ios::failbit);
    }
    return in;
}

int main(int argc, char *argv[])
{
    MAPM a = 5, p = 14, q = "9.8", d;
    
    p /= a;
    q /= a;
    
    d = pow(p/2, 2) - q;
    
    cout << "Diskriminante = " << d << " = " << fixed << d << endl;
           
    system("PAUSE");
    return EXIT_SUCCESS;
}
```

Gruß


----------



## DarkSean (12. Februar 2006)

Super, jetzt funktioniert es. Eine Frage noch, wie kann ich überflüssige Nullen im fixed-Format abschneiden? Außerdem möchte ich die Zahlen nur mit einer bestimmten Anzahl von Stellen angezeigt bekommen, wobei die letzte Ziffer gerundet wird. Wie geht das?


----------



## deepthroat (13. Februar 2006)

Du kannst einfach auf eine bestimmte Genauigkeit (sprich: bestimmte Anzahl von Nachkommastellen) runden. Dazu besitzt die MAPM Klasse eine round() Mehtode.

Die Dokumentation der vorhandenen C Funktionen befindet sich übrigens in der Datei function.ref. Die sollteste dir mal anschauen. Die MAPM Klasse ist einfach in der m_apm.h Datei definiert, so das du da einfach schauen kannst welche Methoden die Klasse sonst noch besitzt. Meist kannst du zu jeder Operation eine Genauigkeit angeben.

Du kannst ja evtl. den Ausgabeoperator weiter anpassen:
	
	
	



```
#include <algorithm>

ostream& operator<< (ostream& out, const MAPM& mapm_var) {
    const int digits = mapm_var.significant_digits();

    char* buffer = new char[digits + 12];
    
    if (mapm_var.is_integer()) {
      mapm_var.toIntegerString(buffer);
    } else if (out.flags() & ios::fixed) {
      mapm_var.toFixPtStringEx(buffer, min(out.precision(), digits));
    } else {
      mapm_var.toString(buffer, min(out.precision(), digits));
    }
    out << buffer;

    delete[] buffer;
    
    return out;
}
```
Ansonsten kannst du die Nullen ja auch selbst noch abschneiden wenn da welche übrig bleiben.

Gruß


----------

