Double vergleichen
-
Hallo ihr Lieben,
ich bin gerade darüber gestolpert, dass mein Standardverfahren eine Funktion zu berechnen leider nicht funktioniert. Geben sei z.B. eine einfache zusammengesetzte Funktion:
#include <iostream> #include <math.h> double f(double & x) { if(x < 1) return 2*x; return x*x; } int main() { double const x_min = -5.0; double const x_max = 5.0; double const dx = 0.1; double x = x_min; while(x < x_max) { std::cout << x << "\t" << f(x) << std::endl; x += dx; } }Wenn ich jetzt an die kritische Stelle
x=1in den Datensatz schaue, dann erblicke ich folgendes0.7 1.4 0.8 1.6 0.9 1.8 1 2 1.1 1.21 1.2 1.44 1.3 1.69Verdammt, für
x=1wurde also noch mit dem linearen Anteil der Funktion gerechnet, weil scheinbarif(x<1)fürx=1keinfalsezurückgibt.
Jetzt habe ich gelesen, dass mandoubleVariablen nicht vergleichen sollte, weil der Vergleich durch die Darstellung nicht eindeutig ist. Vielmehr sollte man im Falle eines Vergleichs mit der Differenz und einer gewünschten Genauigkeit arbeiten.Dies führt mich zu der konzeptionellen Frage: Muss ich das hier auch berücksichtigen? Die offensichtliche Antwort wäre: "Ja, immerhin vergleiche ich in der if-Abfrage zwei
doubles!"Oder gibt es einen eleganteren Weg zur Auswertung einer Funktion für verschiedene Eingabewerte?
Gruß,
-- Klaus.
-
Verdammt, für x=1 wurde also noch mit dem linearen Anteil der Funktion gerechnet, weil scheinbar if(x<1) für x=1 kein false zurückgibt.
1 ist ja auch nicht < 1. 1 ist 1. Nicht kleiner, nicht größer. Vielleicht aber willst du if(x <= 1)?
-
Klaus82 schrieb:
Jetzt habe ich gelesen, dass man
doubleVariablen nicht vergleichen sollte, weil der Vergleich durch die Darstellung nicht eindeutig ist. Vielmehr sollte man im Falle eines Vergleichs mit der Differenz und einer gewünschten Genauigkeit arbeiten.Ja, sowas liest man öfter.
Alle Generalisierungen sind falsch.
Schon bemerkt, daß die Schleife manchmal die Obergrenze erreicht und manchmal nicht bei anderen Schrittweiten?#include <iostream> #include <math.h> double f(double x) { if(x < 1) return 2*x; return x*x; } int main() { for(int i=-5;i<=5;++i) { double x=double(i)/10; std::cout << x << '\t' << f(x) << '\n'; } }
-
Das hat nichts mit dem Vergleich, wohl aber mit den Rundungsfehlern bei der Addition zu tun.
Dein Problem steht in Zeile 23 Deines Programms.
50x +=dx gibt eben nicht 5, sondern etwas anderes, da 0.1 binär nicht exakt darstellbar ist.In Deinem Fall besser wäre:
int main() { double const x_min = -5.0; double const x_max = 5.0; double const dx = 0.1; // double x = x_min; //while(x < x_max) int N = int((x_max - x_min)/dx + 0.5); // Anzahl der Durchläufe bestimmen for( int i=0; i<=N; ++i ) { double x = x_min + i * dx; std::cout << x << "\t" << f(x) << std::endl; // x += dx; } }
-
Hier kannst Du sehen, was bei dir wirklich passiert. Und dann ist auch klar, warum man u.U. mit Delta und Wunschgenauigkeit arbeitet, bzw. warum man wissen sollte, was im Rechner vorsichgeht(siehe volkards Beitrag).
-
Werner_logoff schrieb:
double x = x_min + i * dx;double x = x_min + i * dx;//Teufel-Beelzebub-Alarm
-
volkard schrieb:
Werner_logoff schrieb:
double x = x_min + i * dx;//Teufel-Beelzebub-AlarmNö!

-
Werner_logoff schrieb:
volkard schrieb:
Werner_logoff schrieb:
double x = x_min + i * dx;//Teufel-Beelzebub-AlarmNö!

Mit Schrittweiten 1/7, 1/9 klappts nicht, mit 1/11 und 1/13 klappts. Komisch.
double x = x_min + i*(x_max-x_min) / N;//toi toi toi
-
Aha.
Caligulaminus schrieb:
Hier kannst Du sehen, was bei dir wirklich passiert. Und dann ist auch klar, warum man u.U. mit Delta und Wunschgenauigkeit arbeitet, bzw. warum man wissen sollte, was im Rechner vorsichgeht(siehe volkards Beitrag).
Und die Kernaussage ist, dass ich mir durch beständiges Aufaddieren einen ziemlichen Rundungsfehler einfange?
Werner_logoff schrieb:
volkard schrieb:
Werner_logoff schrieb:
double x = x_min + i * dx;//Teufel-Beelzebub-AlarmNö!

Und wie ist das zu verstehen?
Diese Darstellung kenne ich auch: Ich suche mir ein Intervall, bestimmte die Anzahl der Diskretisierungspunkte und gehe dann wie in dem Zitat vor.Was ist daran der Beelzebub-Faktor oder auch gerade nicht? Scheinbar sind sich Werner_logoff und volkard nicht einig?

Gruß,
-- Klaus.
-
Das Problem wird durch Werners Vorschlag nicht behoben. Probier zum Beispiel mal Schrittweite 1./49. Gleiches Problem, gleiche Ursache. 249*(1./49) < 6.
-
SeppJ schrieb:
Das Problem wird durch Werners Vorschlag nicht behoben. Probier zum Beispiel mal Schrittweite 1./49. Gleiches Problem, gleiche Ursache. 249*(1./49) < 6.
SeppJ meint 294*(1./49)<6
Die 49 ist tatsächlich die erste Zahl bei der der 'Beelzebub' zuschlägt. Ich war bisher der Meinung, dass bei einer reinen Multiplikation oder Division solche Art Rundungsfehler nicht auftreten - aber man lernt ja nie aus.
Interessanterweise habe ich das mal genauso in grauer Vorzeit an der Uni gelernt.@Klaus: da stellt sich natürlich auch die Frage, welche Anforderungen Du an die Iteration stellst.
Die Vorgabe in Deinem Programm sind die (beliebig wählbaren!) Parameterx_min,x_maxunddxund implizit stellst Du anscheinend die Anforderung, dass die Zahl 1 ein Element Deiner Iteration sein soll.
Wie müsste dann volkards Lösung allgemein aussehen - z.B. mit einemdx=0.08?
-
Hallo Werner,
Werner_logoff schrieb:
@Klaus: da stellt sich natürlich auch die Frage, welche Anforderungen Du an die Iteration stellst.
Die Vorgabe in Deinem Programm sind die (beliebig wählbaren!) Parameterx_min,x_maxunddxund implizit stellst Du anscheinend die Anforderung, dass die Zahl 1 ein Element Deiner Iteration sein soll.Das Programm im Eingangspost ist ein kompilierfähgies Minimalbeispiel, anhand dessen ich die Problematik diskutieren wollte.
Unabhängig davon bleibt aber der Kern deiner Aussage bestehen, dass ich in meinem Hauptpogramm auch die Diskretisierung derart wähle, dass ausgezeichnete Werte einer if-Abfrage genau angenommen werden.
Allerdings führte dies bisher nie zu seltsamen Ergebnissen oder einem Runtime-Error o.ä. Glück?Nur nachdem mir dies bei erneutem Nachdenken und Ausprobieren explizit aufgefallen ist, möchte ich natürlich Gewissheit, dass die if Abfrage auch genau das tut was sie soll.
Gruß,
-- Klaus.