O-o-oh: ich habe ein sehr dickes Problem!!!
-
Hallo,
ich habe gerade festgestellt, das ein C-Code von mir auf einem Debian Rechner
sauber laueft, aber auf einem Kubuntu Rechner mir 'faslche' Zahlen ausgibt.Zum Beispiel vergleiche ich einfach sechs double-Zahlen mit "==". Ich glaube
hier liegt das Problem.Kann man irgendwie Rechnerunabhaengig Zahlen vergleichen, oder hat es mit was
anderem zu tun?Vielen Dank und viele Gruesse, simsa
-
simsa schrieb:
Kann man irgendwie Rechnerunabhaengig Zahlen vergleichen, oder hat es mit was
anderem zu tun?Ja, nein, vielleicht oder so.
Woher sollen wir das wissen ohne den Code gesehen zu haben?Ein Test auf Gleichheit bei Fließkommazahlen ist selten eine gute Idee.
Es ist aber auch durchaus möglich, dass der Fehler ganz woanders liegt.Wenn dein Code zu gr0ß ist oder du ihn nicht zeigen kannst/magst:
Reduziere deinen Code solange, bis der Fehler nicht mehr auftritt.
Dann kannst du ein minimales Beispiel erstellen, wo dieser Fehler noch auftritt.
Diese Beispiel kannst du dann posten.
-
Hallo Dirk,
hier ist der Code-Abschnitt:
typedef struct { double x; double y; double z; } vertex; vertex *add_v (vertex *v, vertex new_v, size_t *v_length, size_t *t_index) { size_t i = 0; int chk = 0; /* check if vertex already exists */ for (i=0; i<*v_length; i++) { if ( new_v.x == v[i].x && new_v.y == v[i].y && new_v.z == v[i].z ) { chk = 1; *t_index = i; break; } } /* add new vertex */ if (chk != 1) { v[*v_length] = new_v; *t_index = *v_length; *v_length += 1; } return v; }
wenn ich statt "double" "float" oder "long double" verwende, dann bekomme
ich jedesmal andere Werte.Gruesse, simsa
-
Das Vergleichen von double-Werten ist wie gesagt relativ fragil und nicht absolut plattformunabhängig lösbar.
Du musst Kompromisse in der Genauigkeit deines Vergleiches eingehen; das kannst du z.B. erreichen, indem du statt == eine eigene Funktion spendierst und dort dann quasi deinen eigenen Vergleich implementierst auf Basis "gerundet gleich".int istGleich(double a,double b) { return fabs(a - b) < DIFFERENZ; }
und hier nimmst du stufenweise mal Vergleichswerte für deinen möglichst realen Datenbestand vor.
DBL_EPSILON hierfür zu verwenden ist übrigens falsch, weil der Wert hierfür nichts taugt und nur eine falsche Sicherheit suggeriert.
-
Hallo Wutz,
ich habe das Problem so aehnlich behoben,
ich habe diese Stelle
if ( new_v.x == v[i].x && new_v.y == v[i].y && new_v.z == v[i].z )
mit dieser Stelle ausgetauscht:
if ( fabs(new_v.x-v[i].x)<=16*DBL_EPSILON*fmax(fabs(new_v.x),fabs(v[i].x)) && fabs(new_v.y-v[i].y)<=16*DBL_EPSILON*fmax(fabs(new_v.y),fabs(v[i].y)) && fabs(new_v.z-v[i].z)<=16*DBL_EPSILON*fmax(fabs(new_v.z),fabs(v[i].z)) )
und siehe da, es funktioniert. Ich hoffe nur, dass es auch auf meinem anderen
Rechner funktioniert. Das ist ja echt beschiessen, dass es Plattformabhaengig
ist.Eine andere Frage: wie machen es denn die "Profis", die numerische Programme
fuer alle Plattformen coden?Viele gruesse, simsa
-
Ich habe dir gesagt du sollst nicht == benutzen, das schließt natürlich >=,<=,!= ein.
Ich habe dir gesagt, du sollst nicht DBL_EPSILON benutzen.
Üblicherweise verwenden professionelle Programme plattformzugeschnittene Bibliotheken (OS und/oder CPU spezifisch), auch schon aus Performanzgründen.
Da ist dann natürlich nicht mehr sehr viel plattformunabhängig.
-
Ich habe dir gesagt du sollst nicht == benutzen, das schließt natürlich >=,<=,!= ein.
Der Sprung von == zu <= ist mir jetzt nicht ganz klar.
-
Wutz schrieb:
Ich habe dir gesagt du sollst nicht == benutzen, das schließt natürlich >=,<=,!= ein.
Ich habe dir gesagt, du sollst nicht DBL_EPSILON benutzen.
Üblicherweise verwenden professionelle Programme plattformzugeschnittene Bibliotheken (OS und/oder CPU spezifisch), auch schon aus Performanzgründen.
Da ist dann natürlich nicht mehr sehr viel plattformunabhängig.Der Test sieht mir so implementiert auch sehr solide aus. Ich weiß nicht, was du hast. Natürlich ist DBL_EPSILON alleine fragil, aber hier wird noch eine Anpassung der Größenordnung durchgeführt, und damit passt das schon wieder.
und auch <=/>= als Schranke auf die absolute differenz ist völlig okay.
-
Prinzipiell kann man das so aufziehen. Ob ich mich von DBL_EPSILON abhängig machen würde, hängt vom umgebenden Code ab. Ich sehe aber ein grundsätzliches Problem: Wenn du mal Null mit Null vergleichst, geht dein Epsilon (weil es von der Größenordnung der Eingabewerte abhängt) gegen Null. Dann können Rundungsfehler dich wieder kalt erwischen.
Eine einfache Methode, das zu umgehen, wäre, in der Umgebung von Null ein festes Minimal-Epsilon zu nehmen. Beispielsweise
int epsilon_equal(double x, double y, double eps) { return fabs(x - y) < fmax(eps, eps * fmax(fabs(x), fabs(y))); } ... if(epsilon_equal(new_v.x, v[i].x, 1e-10)) { ... }