Überlauf eines long double?
-
Hallo!
Ich habe ein Problem mit einer Planetensimulation. Ich will den Flächeninhalt bestimmen, der von der verbindungslinie zwischen planet und Sonne überstrichen wird. wie man sich denken kann, kommen riesige Zahlen raus (die Erde ist 150 Mio. km von der Sonne entfernt). Ich berechne diese Fläche, indem ich sie in viele Dreiecke aufteile, die einzeln mit dieser Funktion berechnet werden:
long double Flaeche(long double xa, long double ya, long double xb, long double yb, long double xc, long double yc) {long double a = sqrtl(powl((xb-xc), 2.0) + powl((yb-yc), 2.0)); long double b = sqrtl(powl((xc-xa), 2.0) + powl((yc-ya), 2.0)); long double c = sqrtl(powl((xa-xb), 2.0) + powl((ya-yb), 2.0)); long double s = (a+b+c)/2.0; long double Flaecheninhalt = (sqrtl((s*(s-a)*(s-b)*(s-c)))); if (Flaecheninhalt<0){ Flaecheninhalt = Flaecheninhalt * (-1.0);} return Flaecheninhalt; }Die Teilflächen werden dann addiert.
Dabei kommen aber manchmal Zahlen raus, die kleiner als 0 sind. Die if-Abfrage
if (Flaecheninhalt<0){ Flaecheninhalt = Flaecheninhalt * (-1.0);}bemerkt dies aber nicht.
Läuft der long double über?
-
Also ich sehe in der Funktion nichts, was negative Werte hervorrufen könnte - evnetuell würde es helfen, wenn du die einzelnen Werte mal im Debugger betrachtest. Und wenn ein Gleitkommawert "überläuft", bekommst du übrigens keine negativen Werte heraus, sondern 1.#inf (unendlich).
-
(s*(s-a)(s-b)(s-c)))
könnte dies negativ werden?

-
nein.
-
Ersetze doch mal die powl((xb-xc), 2.0) Aufrufe durch einfache Multiplikationen. Weiterhin kannst du um Überläufe zu vermeiden die Wurzel der Prudukte durch ein Produkt von Wurzeln ersetzen. Intern werden dann kleinere Werte verarbeitet. Also statt
sqrtl((s*(s-a)(s-b)(s-c)))
besser
sqrtl(s)*sqrtl(s-a)*sqrtl(s-b)*sqrtl(s-c))
-
Vielen dank für die Hilfe. Ich habe aber glaube ich das eigentliche Problem entdeckt: Ich schreibe die Ergebnisse in eine Datei. Wie kann ich C++ dazu bringen, die Zahlen ordentlich formatiert in eine Datei zu schreiben? Ich habe folgendes Experiment gemacht:
long double i = 9.21369875421325665478; char zwischenspeicher[1000]; FILE *fl; fl = fopen("Flächeninhalte.txt", "w"); sprintf(zwischenspeicher, "%g\n", i); fprintf(fl, zwischenspeicher); fclose(fl);In der Datei stand jedoch -3.96111e-215 ...
Kann mir jemand helfen?
-
Wenn du schon mit C++ arbeitest, dann nimm auch C++ Methoden (ja, man könnte auch fprintf() soweit zurechtbiegen, aber wozu der aufwand?):
ofstream f1("Flächeninhalte.txt"); f1<<i<<endl;(btw, du kannst die fprintf()-Ausgaben genauso formatieren wie bei sprintf() ;))
-
Ich habs ausprobiert, das Programm schreibt einen falschen Wert in die Datei. Statt 9.21369875421325665478 schreibt es -3.96111e-215.
-
Und was sagt dein Debugger zu dieser Variablen?
-
Ich komme mit dem dev-c++ debugger nicht zurecht. wie kann ich eine Variable beobachten und das Programm Zeile für Zeile ausführen?
-
ich also weis ja nicht genau was bei A B C für differenzen auftreten können, aber bin der Meinung das s*(s-a)(s-b)(s-c) auch negativ werden kann, aufgelöst sollte das so aussehen
s^4 + (s^3)*a + (s^2)*b*c + s*a*b*c - (s^3)*b - (s^3)*c - (s^2)*a*b -(s^2)*a*c
was zeigt das bei dem Minusteil das C mit höher ptonzierten S multipliziert wird, daraus folgt ein sehr großen c bei sehr kleinem a und b wird s durch die formel s=(a+b+c)/2; kleiner c und das um einiges. Daher kommen evtl falsch eingelesene wete rein oder der gleichen, weil bei einem dreieck (a,b,c) sollte das normal nicht auftreten. jeddoch lieben wir alle das programmieren weil dinge passieren die so nicht passieren sollten oder^^? Grundprinzipiel kann die formel negative werte ausgeben, ausser ich habe falsch aufgelöst und normal sollte sie aber bei seitenlänmgen a,b,c eines dreiecks immer positiv sein.
-
Dann solltest du den Wert vorher testen, bevor du die Wurzel ziehst, denn die Wurzel einer negativen Zahl ist nun mal nicht definiert und erzeugt bei Aufruf von sqrt() eine std::domain_error Ausnahme.
-
@oelsni
Nun verrat mir bitte mal wie bei einen Dreieck eine Seite sehr groß und die beiden anderen sehr klein sein sollen?
-
Ich habe das Beispielprogramm (siehe vorletzter post von mir) mit dem Debugger getestet. Der Debugger zeigt die long double Variable richtig an, sobald ich sie aber in die Datei schreibe, steht ein ganz anderer Wert in der Datei.
Weiß einer, wie ich das verhindern kann?
-
Wie schreibst du denn jetzt den Wert in die Datei (mittels fstream)?
Evtl. mußt du noch einige Formatierungsflags (fixed oder scientific) angeben?
-
Mein Programm sieht so aus:
long double i = 9.21369875421325665478; ofstream f1("Flächeninhalte.txt"); f1<<i<<endl;
-
Braunstein schrieb:
@oelsni
Nun verrat mir bitte mal wie bei einen Dreieck eine Seite sehr groß und die beiden anderen sehr klein sein sollen?krümmung des raums aufgrund von masse-singularitäten
vielleicht sind die berechnungen ja allesamt richtig. nur unser verstand ist zu klein, um dies zu begreifen

-
kann mir jemand erklären, wie man einen long double in einen string umwandelt? Oder gibt es einen anderen Weg, um einen double korrekt(!) in eine Datei zu schreiben? Ich komm mit meinem Programm and der Stelle echt nicht weiter.
-
Hast du auch in der richtigen datei nachgeschaut? Dein Code funktioniert bei mir ohne Probleme.
#include <iostream> #include <sstream> #include <iomanip> using namespace std; int main() { long double i = 9.21369875421325665478; stringstream f1; f1 << std::setprecision(15) << i << endl; //mehr als 15 Stellen sind auch bei long double nicht drin cout << f1.str() << endl;; return 0; }
-
Ich habe den Code ausprobiert, bei mir zeigt er eine ganz falsche Zahl am Bildschirm an (dasselbe Problem, wie vorher in der Datei). Woran kann das liegen (ich nutz Dev-C++)? Muss ich vielleict die IDE wechseln? Wenn ja, welche kostenlosen IDEs würdet ihr mir empfehlen?