0 Daumen
105 Aufrufe

Hallo an alle, die das lesen.

Ich habe eine rechenintensiven Anwendung in C++ "geerbt" und stoße beim Testen ständig auf irgendwelche seltsamen Fehler, die ich aber alle auf eine ähnliche Ursache eingrenzen konnte.

Ich berechne in einer Funktion eine mathematische Funktion und gebe den Wert zurück:

double Berechne( double x ) {
double f= cos(x);
return f;
}

Nun übergebe ich diesen Wert an eine Test-Funktion, wo er geprüft wird:

void Check( double x, double f ) {
if (f==cos(x)) {
//Test ok
} else {
//Test fehlgeschlagen
}
}

Mein Problem ist, das der Test immer fehlschlägt.

Kann mir bitte jemand helfen? Ich verstehe es einfach nicht.

Danke und Grüße

von

Gib ein vollständiges Programm an.

Bei meinem werden alle Tests bestanden:

#include <iostream>
#include <cmath>
#include <sstream>

using namespace std;

double Berechne( double x ) {
double f= cos(x);
return f;
}

void Check( double x, double f ) {
if (f==cos(x)) {
cout << "Test ok\n";
} else {
cout << "Test fehlgeschlagen\n";
}
}

int main(int argc, char* argv[]) {
double x;
auto ss = stringstream(argv[1]);

ss >> x;

Check(x, Berechne(x));
}

Vermutlich ersetzt dein C++-Compiler die Funktion "Berechne" als inline-Funktion durch \(\cos(x)\). Dann lautet der Vergleich \((\cos(x)==\cos(x))\) und ist immer true.

Man müsste erzwingen, dass der Compiler den Wert nicht in einem Register bzw. auf dem Fließkomma-Stack halten darf. Ich würde versuchen, die Variable \(f\) als globale Variable mit

static volatile double f;

anzulegen. Dann müsste die Funktion "Berechne" den Wert eigentlich in den Speicher zurückschreiben.

Ich habe eine andere Vermutung, nämlich irgendetwas mit What Every Programmer Should Know About Floating-Point Arithmetic. Ob die stimmt, werden wir aber nicht erfahren solange es kein SSCCE gibt.

1 Antwort

0 Daumen
 
Beste Antwort

Aloha :)

Willkommen in der Stacklounge... \o/

Beim Vergleich von Fließkommazahlen auf Gleichheit ist stets Vorsicht geboten, da es durch Abschneiden der Binär-Mantisse und durch Normierungen der Binär-Mantisse zu Rundungsfehlern kommen kann.

In deinem Code ist dieser Effekt jedoch unwahrscheinlich, weil die Bitmuster bei beiden Berechnungen von \(\cos(x)\) identisch sein sollten.

Meine Vermutung ist Folgende:

Im IEEE754-Standard gibt es das Format "long double" mit intern 80 Bits. Intel- und AMD-Prozessoren rechnen per Default-Einstellung intern immer mit diesem Format, auch wenn die Fließkomma-Werte im Format "double" oder "float" vorliegen. Beim Speichern eines Wertes vom Fließkomma-Stack der CPU ins RAM werden die "long double"-Werte dann in das jeweilige Format "double" oder "float" runtergerechnet.

Beim Vergleich \(f==\cos(x)\) liegen auf dem Fließkomma-Stack des Prozessors 2 Werte:

Der Wert für \(f\) stammt aus dem Speicher, lag also im "double"-Format vor und wurde auf dem Fließkomma-Stack in das "long double"-Format konvertiert, wobei die Mantisse mit Nullbits aufgefüllt wird.

Der gerade berechnete Wert für \(\cos(x)\) liegt ebenfalls im "long double"-Format vor, allerdings sind hier alle Stellen der "long double"-Mantisse frisch berechnet und gültig, sodass sich die Mantissen in den zuvor aufgefüllten Nullbits unterscheiden.

Du kannst die Vermutung prüfen, indem du den Vergleich leicht abänderst:

if (f==(double)cos(x)) ...

Ein kleines Feeback, ob ich richtig liege, wäre schön.

von

Danke für eure Hilfe.

Ich wollte nur kurz Rückmeldung geben, dass die Typumwandlung in double tatsächlich geholfen hat. Jetzt laufen die Tests durch.

Gibt es in C/C++ auch einen Vergleich, der nicht nur die Gleichheit der Werte, sondern auch die Gleichheit der Datentypen prüft, ähnlich wie in JavaScript?

Das würde ja dann auch helfen.

In C gibt es einen solchen Vergleich nicht.

In C++ bin ich mir nicht ganz sicher, die fummeln da alle 2 oder 3 Jahre am Standard rum. Meines Wissens nach, gibt es das aber auch nicht in C++.

Ein anderes Problem?

Stell deine Frage

Willkommen bei der Stacklounge! Stell deine Frage einfach und kostenlos

x
Made by a lovely community