Dr. Erhard Henkes, Stand: 22.04.2023

Zurück zum Start


Konsolen-Programme mit C++

Wir werden die grundlegenden Fähigkeiten zum Schreiben eines Konsolen-Programms nun an kleinen Rechenprogrammen weiter ausbauen.
 

1.4. Kleine Rechenprogramme, float und double

Ein einfaches Beispiel aus der Mathematik wollen wir uns gemeinsam anschauen.

Hat man eine quadratische Gleichung x² + px + q = 0, so löst sich diese auf nach
x1 = - p/2 + sqrt( p²/4 - q) und x2 = - p/2 - sqrt( p²/4 - q).


sqrt
steht hier für das Ziehen der Quadratwurzel (square root) und erfordert den Header <cmath>.

Zunächst soll unser Programm anzeigen, was es auszuführen gedenkt, anschließend die Eingabe von p und q fordern, die Berechnung durchführen und zum Schluß die Ergebnisse x1 und x2 ausgeben. Da wir hier mit Kommazahlen anstelle von Ganzzahlen arbeiten müssen, benötigen wir einen anderen Variablentyp als int . Dafür gibt es die entsprechenden Typen für Fließkommazahlen float oder double.

Der Datentyp float (4 Byte = 32 bit) verwendet für die Mantisse 24 bit, für den Exponenten 7 bit und für das Vorzeichen ein bit und kann daher Werte im Bereich 3.4 * 10-38 bis 3.4 * 1038 aufnehmen. Die Darstellung ist auf 6 Nachkommastellen begrenzt.

Der mächtigere Datentyp double (8 Byte = 64 bit) verwendet für die Mantisse 53 bit, für den Exponenten 10 bit und für das Vorzeichen ein bit und kann daher Werte im Bereich 1.7 * 10-308 bis 1.7 * 10308 aufnehmen. Die Darstellung ist bis auf 15 Nachkommastellen genau. Daher ist dieser Typ z.B. für einfache kaufmännische, technische und wissenschaftliche Programme geeignet.

Damit wir sqrt() benutzen können, müssen wir cmath (Datei math.h) einbinden.
Damit erhalten wir im ersten Ansatz folgenden Sourcecode:
 
#include <iostream>
#include <limits>
#include <cmath>  // notwendig fuer sqrt(...)
using namespace std;

void wait()
{
  std::cin.clear();
  std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
  std::cin.get();
}

int main()
{
 cout << "Berechnung quadratischer Gleichungen vom Typ x*x + p*x + q = 0"
      << endl << endl;

 double p, q, x1, x2;
 cout << "Bitte p eingeben: ";
 cin  >> p;
 cout << "Bitte q eingeben: ";
 cin  >> q;

 x1 = -p/2 + sqrt( p*p/4.0 - q );
 x2 = -p/2 - sqrt( p*p/4.0 - q );

 cout << endl << "x1 = " << x1 << endl << "x2 = " << x2 << endl;

 wait();
}

Hier folgt ein erstes Rechenbeispiel mit diesem Programm:

Vielleicht fragen Sie sich, warum wir p*p/4.0 und nicht einfach p*p/4 geschrieben haben. Dies ist ein wichtiger Punkt!

Das Anfügen von ".0" wandelt Ganzzahlen in Fließkommazahlen um. Kommen in einem Ausdruck sowohl ganze Zahlen als auch Kommazahlen vor, so stellt der Compiler sicher, dass zu jeder Rechnung auf beiden Seiten des Rechenzeichens der gleiche Typ vorliegt. Hierzu wird z.B. eine ganze Zahl in eine Fließkommazahl umgewandelt.

In obigem Fall spielt es keine Rolle, ob wir 4 oder 4.0 schreiben, aber es gibt auch tückische Fälle, die ohne Warnung zu falschen Resultaten führen. Hätten wir z.B. nicht geschickt p*p/4.0 - q codiert, sondern 1/4*p*p - q, dann wäre die Misere sofort da!

Probieren Sie unser Programm mit folgenden fehlerhaften Zeilen aus:

x1 = -p/2 + sqrt( 1/4*p*p - q );
x2 = -p/2 - sqrt( 1/4*p*p - q );

Wie Sie sehen, ist das Ergebnis falsch! Woran liegt das genau?

Der Grund liegt darin, dass 1/4 als Ergebnis 0 liefert. 1 und 4 sind Ganzzahlen, daher wird das Ergebnis von 1/4 auch als Ganzzahl gewertet, und hier wird 0.25 durch Abrundung umgewandelt in 0. Testen Sie das Programm zur Überprüfung mit folgenden Zeilen:

x1 = -p/2 + sqrt( 0 - q );
x2 = -p/2 - sqrt( 0 - q );

Wie Sie sehen, erhalten Sie obiges falsches Ergebnis. Daher muss(!) man die Integer-Schreibweise 1/4 in die float/double-Schreibweise 1.0/4.0 umwandeln, dann klappt das:

x1 = -p/2 + sqrt( 1.0/4.0*p*p - q );
x2 = -p/2 - sqrt( 1.0/4.0*p*p - q );

Machen Sie sich diesen Zusammenhang bitte vollkommen durch eigene Experimente klar.
 

Fazit:
In Kombination mit Fließkommazahlen sollte man ganze Zahlen immer mit dem Anhang .0 versehen, damit sie als Fließkommazahlen behandelt werden und man korrekte Resultate erhält.

Wir wollen nun auch auf den Fall eingehen, wenn  p*p/4.0 - q negativ ist.
Sie wissen ja bereits, wie man mit if/else solche Fälle unterscheiden und abfangen kann:

 
#include <iostream>
#include <limits>
#include <cmath>  // notwendig fuer sqrt(...)
using namespace std;

void wait()
{
  std::cin.clear();
  std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
  std::cin.get();
}

int main()
{
 cout << "Berechnung quadratischer Gleichungen vom Typ x*x + p*x + q = 0"
      << endl << endl;

 double p, q, x1, x2;
 cout << "Bitte p eingeben: ";
 cin  >> p;
 cout << "Bitte q eingeben: ";
 cin  >> q;

 double discriminant = p * p / 4.0 - q;
 
 
if (discriminant < 0)
 {

   cout << endl << "Die Gleichung hat keine reellen Lösungen." << endl;
 }
 else
 {

   x1 = -p / 2 + sqrt(discriminant);
   x2 = -p / 2 - sqrt(discriminant);
  
cout << endl << "x1 = " << x1 << endl << "x2 = " << x2 << endl;
   }

 wait();
}

Jetzt stellt sich allerdings die Frage, wie baut man dieses Programm auf, wenn man auch aus negativen Zahlen die Quadratwurzel ziehen möchte.
Dazu benötigt man den bereich der imaginären bzw. komplexen Zahlen:

 
#include <iostream>
#include <limits>
#include <cmath>  // notwendig fuer sqrt(...)
using namespace std;

void wait()
{
  std::cin.clear();
  std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
  std::cin.get();
}

int main()
{

  cout << "Berechnung quadratischer Gleichungen vom Typ x*x + p*x + q = 0"
       << endl << endl;


  double p, q;
  cout << "Bitte p eingeben: ";
  cin >> p;
  cout << "Bitte q eingeben: ";
  cin >> q;

  complex<double> x1, x2;
  double discriminant = p * p / 4.0 - q;

  if (discriminant < 0)
  {

    x1 = complex<double>(-p/2,  sqrt(-discriminant));
    x2 = complex<double>(-p/2, -sqrt(-discriminant));
    cout << endl << "x1 = " << x1 << endl << "x2 = " << x2 << endl;
  }
  else
  {

    x1 = complex<double>(-p/2 + sqrt(discriminant), 0);
    x2 = complex<double>(-p/2 - sqrt(discriminant), 0);
    cout << endl << "x1 = " << x1.real() << endl << "x2 = " << x2.real() << endl;
     }

  wait();
}

Dieses Programm verwendet die <complex> Bibliothek, um komplexe Zahlen zu verarbeiten.
Ist der Diskriminant kleiner als Null, werden die beiden Lösungen als komplexe Zahlen berechnet und ausgegeben.
Andernfalls werden sie als reelle Zahlen berechnet und ausgegeben.



Übungsaufgabe:
Basteln Sie sich eine angepasste Routine für die Gleichung a*x2 + b*x + c:

Die Ausdrücke

   (-b + sqrt(4*a*c - b*b))/2/a
   (-b - sqrt(4*a*c - b*b))/2/a

stehen für die Nullstellen des quadratischen Polynoms a*x2 + b*x + c (falls vorhanden).


Wir betrachten noch ein weiteres einfaches Beispiel zur Berechnung des Kugelvolumens aus dem Radius:
 
#include <iostream>
#include <limits>
#include <cmath>
// notwendig fuer M_PI und pow(x,y)
using namespace std;

void wait()
{
  cin.clear();
  cin.ignore(numeric_limits<streamsize>::max(), '\n');
  cin.get();
}

int main()
{
  double r, v;
  cout << "Berechnung des Kugelvolumens: " << endl;
  cout << "Bitte Radius eingeben." << endl;
  cout << "Radius = ";
  cin >> r;
  v = 4.0/3.0 * M_PI * pow(r,3);
//Formel
  cout << "Das Volumen betraegt " << v;
  cout << endl;
  wait();

}

Hier wird die in der Mathematik wichtige Konstante Pi benötigt, die in cmath bereits definiert wurde.
Solche Konstanten schreibt man möglichst groß, um sie von Variablen zu unterscheiden.



Übungsaufgabe:

Schreiben Sie ein Programm, das die Berechnung des Umfangs

U = 2.0 * Pi * r

und die Fläche des Kreises mit Radius r

F = r * r * Pi

ermöglicht.



Als interessante Ergänzung folgt hier die Lösung einer kubischen Gleichung gemäß der Cardan-Methode:

 
#include <iostream>
#include <limits>
#include <cmath> // notwendig fuer sqrt(...) und pow
#include <complex> // notwendig fuer komplexe Zahlen
using namespace std;

void wait()
{
  cin.ignore(numeric_limits<streamsize>::max(), '\n');
  cout << "Druecken Sie die Eingabetaste, um fortzufahren...";
  cin.get();
}

int main()
{
  cout << "Berechnung kubischer Gleichungen vom Typ x^3 + p*x^2 + q*x + r = 0"
       << endl << endl;


  double p, q, r;
  cout << "Bitte p eingeben: ";
  cin >> p;
  cout << "Bitte q eingeben: ";
  cin >> q;
  cout << "Bitte r eingeben: ";
  cin >> r;

  complex<double> x1, x2, x3;
  double a = q - p * p / 3.0;
  double b = 2 * pow(p / 3.0, 3) - p * q / 3.0 + r;
  complex<double> delta = pow(b / 2.0, 2) + pow(a / 3.0, 3);
  complex<double> u = pow(-b / 2.0 + sqrt(delta), 1.0 / 3.0);
  complex<double> v = pow(-b / 2.0 - sqrt(delta), 1.0 / 3.0);
  x1 = u+v - p / 3.0;
  x2 = -(u+v) / 2.0 - p / 3.0 + (u-v) * sqrt(3.0) * complex<double>(0,1) / 2.0;
  x3 = -(u+v) / 2.0 - p / 3.0 - (u-v) * sqrt(3.0) * complex<double>(0,1) / 2.0;

  cout << endl << "x1 = " << x1 << endl << "x2 = " << x2 << endl
       << "x3 = " << x3 << endl;


  wait();
}

Das Programm löst kubische Gleichungen vom Typ x^3 + p*x^2 + q*x + r = 0 mit der Cardan-Methode.
Diese Methode verwendet eine Substitution, um die kubische Gleichung in eine reduzierte Form zu bringen,
die keine quadratischen Terme enthält.
Die reduzierte Form lautet x^3 + a*x + b = 0,
wobei a und b aus den ursprünglichen Koeffizienten p, q und r berechnet werden.

Die Lösungen der reduzierten Gleichung können dann mit der Cardan-Formel berechnet werden.
Diese Formel verwendet die Wurzeln einer quadratischen Gleichung, die als u und v bezeichnet werden.
Die Wurzeln u und v werden aus dem Diskriminanten der quadratischen Gleichung berechnet, der als delta bezeichnet wird.

Sobald die Wurzeln u und v berechnet sind, können die drei Lösungen der kubischen Gleichung
mit den folgenden Formeln berechnet werden:

x1 = u + v - p / 3.0
x2 = -(u + v) / 2.0 - p / 3.0 + (u - v) * sqrt(3.0) * complex<double>(0, 1) / 2.0
x3 = -(u + v) / 2.0 - p / 3.0 - (u - v) * sqrt(3.0) * complex<double>(0, 1) / 2.0

Das Programm gibt die drei berechneten Lösungen aus.


AA

 

A

Zurück zum Start