Herzlich Willkommen, lieber Gast!
  Sie befinden sich hier:

  Forum » C / C++ (ANSI-Standard) » Gleitkommazahlen - Problem mangelnder Genauigkeit wie lösen?

Forum | Hilfe | Team | Links | Impressum | > Suche < | Mitglieder | Registrieren | Einloggen
  Quicklinks: MSDN-Online || STL || clib Reference Grundlagen || Literatur || E-Books || Zubehör || > F.A.Q. < || Downloads   

Autor Thread - Seiten: > 1 <
000
13.12.2006, 01:08 Uhr
Steve06



Hallo,

ich programmiere in C++ und glaube, dass ich ein Präzisionsproblem habe.

Ich hole mal etwas weiter aus, um mein Problem zu beschreiben.

Folgendes lineare Gleichungssystem (LGS) mit 2 Gleichungen, 2 Unbekannen (x, y) und den Konstanten Dx, Dy, Vx, Vy ist für einen größeren Datensatz zu lösen.

1 + x * Dx + y * Dy = 0
x * Vx + y * Vy = 0


Umgeformt erhalte ich

x = - y* (Vy/Vx)
y= -1/((Vy*Dx)/Vx - Dy)

So, wenn ich nun x und y für eine Datenreihe gegebener Dx,Dy,Vx,Vy berechne und zur PROBE mal die Werte wieder in die Ausgangsform des LGS oben einsetze, erhalte ich für viele, aber nicht für alle (!) Datenzeilen eine Null auf der rechten Seite der beiden Gleichungen.
In ziemlich vielen Fällen erhalte ich Werte wie 5.55E-17 oder 1.67E-16.
Zuerst habe ich gedacht, es muss sich um einen Programmierfehler handeln, aber dann würde doch nicht für viele das richtige (nämlich genau 0) in der Probe herauskommen?!

Ok, zugegeben, die Abweichung von der null ist schon recht gering.

Mir ist klar, dass ein Computer wegen seiner binären Struktur nicht unendlich viele Nachkommastellen speichern kann. Vermutlich ist beim Speichern der Zwischenergebnisse etwas verloren gegangen.

Ich frage Euch nun nach Möglichkeiten, das Problem zu verringern:
1. Welche Datentypen verwende ich am besten? Ich hatte zuvor double, habe aber alle beteiligten auf long double umgestellt, jedoch ohne Erfolg, d.h. ich konnte keine Veränderung feststellen!
2. Wenn man Formeln im Quellcode hinschreibt mit + - / * ( ) etc., gibt es da irgendwelche Kniffe zu beachten, damit bei den Zwischenschritten nichts verloren geht?
3. Gibt es ein paar gute alte Tricks wie z.B. einfach an irgendeinem Punkt alles mit einer großen Zahl wie 1000000000 zu multiplizieren, um die Werte nach dem Komma, die jenseits der Präzision des Datentyps liegen, "nach vorn zu holen"?
4. Gibt es eine Möglichkeit zu prüfen, ob Werte nur insignifikant von null abweichen und diese dann eben auf genau 0 zu setzen? Z.B. mal den Wert nach der 15. Nachkommastelle abschneiden und dann schauen, ob die "verstümmelte" Zahl genau 0 entspricht. Mit welchem Befehl würde man den Wert beschneiden?

Wenn mich jemand in dieser Thematik aufklären kann, bin ich sehr dankbar.
Habe auch gesehen bei Google, dass es einige sog. "High oder Doubledouble Precision" Libraries für C gibt, aber ich würde gerne auf so ein Addon, dass ich als zusätzlichen Ballast empfinde, verzichten. Wenn jedoch jemand eine gute, schlanke, effiziente, unkomplizierte Library hierzu empfehlen kann, nur her damit .

Beste Grüße und danke für Euer Feedback
Steve
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
001
13.12.2006, 08:41 Uhr
Blubber2063



Also double gibts glaube ich keine long variante von, weil der Datentyp eh schon 64bit breit ist, wenn ich mich jetzt nicht falsch erinnere. Du kannst nur dann genauer werden, wenn du einen größeren Datentyp wählst, alles andere bringt rein gar nichts. Du bist genauer je kleiner die Zahl ist(am besten mit wenig Stellen). Das liegt an der Form wie Gleitpunktformate vom Rechner behandelt werden. Schau dich mal nach floating Point Rechnung um. Wenn du also viel genauer werden willst musst du dir nen größeren Datentypen besorgen.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
002
13.12.2006, 09:11 Uhr
ao

(Operator)


@Steve06:

1. Fehler in der von dir genannten Größenordnung sind normal und unvermeidlich, und du hast ja auch schon verstanden, wieso.

2. Der Typ long double ist vom Standard vorgesehen, allerdings ist es dem Compiler freigestellt, ihn zu implementieren oder stattdessen einfach double zu verwenden.

3. Über die Reihenfolge von Operationen kann man nur Hinweise geben: Was man einmal nach rechts rausgeschoben hat, kann man nicht wieder zurückholen. Darum würde ich versuchen, die Größenordnung der Zahlen so lange wie möglich beizubehalten, also nicht erst alles aufmultiplizieren über mehrere Zehnerpotenzen und dann wieder runterteilen. Und vermeiden, sehr große und sehr kleine Zahlen zu addieren (1E20 + 1 bleibt 1E20).

Kann aber sein, dass deine Zahlenwerte so verstreut sind, dass das gar nicht planbar ist.

4. Du musst einen sog. "Epsilon-Vergleich" implementieren:

C++:
bool double_equal (double a, double b, double eps);


der true liefert, wenn a und b sich um höchstens eps unterscheiden. Dabei versteht man eps sinnvollerweise nicht absolut, sondern als Prozentwert von a bzw. b, so dass bei

C++:
bool b = double_equal (1E20, 1.00000001E20, 1E-6); // Vergleich auf 6 Stellen

true herauskommt.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
003
13.12.2006, 21:53 Uhr
~Steve06
Gast


vielen Dank an Euch beide für Eure Antworten!

beruhigend zu hören, dass das "ganz normal" ist.

ich benutze visual studio 2005, da müsste long double als 80bit wert implementiert sein, wenn ich nicht irre.

ich denke ich brauche nicht groß was anzupassen, da es ja nur um die probe ging. Hauptsache die eigentlichen x,y-werte sind im rahmen der genauigkeit korrekt.

der epsilon-vergleich ist ne klasse sache.

grüße

steve
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
Seiten: > 1 <     [ C / C++ (ANSI-Standard) ]  


ThWBoard 2.73 FloSoft-Edition
© by Paul Baecher & Felix Gonschorek (www.thwboard.de)

Anpassungen des Forums
© by Flo-Soft (www.flo-soft.de)

Sie sind Besucher: