Gleitkomma Fehler wegoptimieren

port29

deus.Server
Code:
#include <stdio.h>

int main (void)
{
  double j;

  for( j = 0.0; j < 5.0; j += 0.1 )
    if( j == 2.0 )
      printf( "Habs\n" );

  return 0;
}

Ein gutes Beispiel, wo die Mathematik nichts dem Programmieren zutun hat. Wegen der Gleitkommazahl kommt da nie "Habs" raus, obwohl die Mathematik etwas anderes behauptet. Aber gibt es da keine Möglichkeit, dass dort das richtige Ergebnis rauskommt? Ich meine, der Compiler sieht ja, dass es sich hierbei um eine Schleife handelt, die terminiert und eine if abfrage, die beim 20. Ausführen etwas ausgibt. Gibt es keine Möglichkeit, dass der Compiler daraus "das Richtige" baut?
 
Code:
#include <stdio.h>

int main (void)
{
  double j;

  for( j = 0.0; j < 5.0; j += 0.1 )
    if( j == 2.0 )
      printf( "Habs\n" );

  return 0;
}

Ein gutes Beispiel, wo die Mathematik nichts dem Programmieren zutun hat. Wegen der Gleitkommazahl kommt da nie "Habs" raus, obwohl die Mathematik etwas anderes behauptet. Aber gibt es da keine Möglichkeit, dass dort das richtige Ergebnis rauskommt? Ich meine, der Compiler sieht ja, dass es sich hierbei um eine Schleife handelt, die terminiert und eine if abfrage, die beim 20. Ausführen etwas ausgibt. Gibt es keine Möglichkeit, dass der Compiler daraus "das Richtige" baut?
Nein. Gleitkommazahlen sind nunmal ungenau. Siehe http://www.cprogramming.com/tutorial/floating_point/understanding_floating_point_representation.html

Entweder du nutzt eine Bibliothek die Gleitkommazahlen mit beliebiger Genauigkeit anbietet, verwendest für die Schritte Integervariablen oder du definierst einen Wert welcher eine obere Grenze des Rundungsfehlers beim Runden angibt und vergleichst ungenau:
C:
#define EPSILON 2.3e-16  

if (abs(j - 2.0) < EPSILON)
  puts("habs");
Gruß
 
Hi,

wie schon gesagt, ich weiß, was an dem Programm falsch ist, ich weiß auch, wie man es besser / richtig macht. Mir ging es bei meiner Frage nur darum, ob man das Programm nicht trotzdem dazu bringen kann, "Habs" auszugeben, obwohl es da sicherlich nicht rauskommen darf. Ich meine, wie oft hatte ich schon Programme in der Hand, die mit -O3 kompilliert, andere Ergebnisse lieferten.

Eine Schleife ist für einen Rechner eh ein Aufwand. Aus kleinen Schleifen macht der Compiler eh Sequenzielle anweisungen. Schleifen ohne Inhalt werden ja auch wegoptimiert. Da dachte ich, man könnte den Kompiler auf irgendeiner sehr hohen Optimierungsstufe so viel Logik hat und die Schleife und das if (das ja eigentlich nichts macht), einfach wegzuoptimieren.
 
Du meinst, der Compiler könnte sofort "printf("habs"); schreiben und die Schleife wegoptimieren?
Aber du siehst ja, er kommt auch mit Schleife nicht dahin.
Beim Vergleich zweier Dezimalzahlen musst du ohne wenn und aber eine Toleranz einkalkulieren, wie deepthroat schon schrieb. Aber es muss fabs() sein, nicht abs(). Letzteres ist für Integer.
 
Hi,

wie schon gesagt, ich weiß, was an dem Programm falsch ist, ich weiß auch, wie man es besser / richtig macht. Mir ging es bei meiner Frage nur darum, ob man das Programm nicht trotzdem dazu bringen kann, "Habs" auszugeben, obwohl es da sicherlich nicht rauskommen darf. Ich meine, wie oft hatte ich schon Programme in der Hand, die mit -O3 kompilliert, andere Ergebnisse lieferten.

Eine Schleife ist für einen Rechner eh ein Aufwand. Aus kleinen Schleifen macht der Compiler eh Sequenzielle anweisungen. Schleifen ohne Inhalt werden ja auch wegoptimiert. Da dachte ich, man könnte den Kompiler auf irgendeiner sehr hohen Optimierungsstufe so viel Logik hat und die Schleife und das if (das ja eigentlich nichts macht), einfach wegzuoptimieren.

Wenn ein Programm auf einer höheren Optimierungsstufe andere Ergebnisse liefert dann deutet das wohl eher auf den ein oder anderen Fehler im Programm selbst hin. (Oder in selteneren Fällen auf einen Fehler im Optimizer).

Genaugenommen darf Dein Beispiel nie "habs" ausgeben, egal wie hoch es optimiert wird, weil "j" nie genau 2.0 erreichen kann (in diesem Beispiel). Deine Grundaussage, die Mathematik würde hier etwas anderes Behaupten ist falsch. Für die Zahlenmegnge der double-Zahlen ist die Mathematik eindeutig und schlüssig. Diese sind eine Untermenge der reellen Zahlen (http://de.wikipedia.org/wiki/Reelle_Zahl). Würde double tatsächlich alle reellen Zahlen abbilden wäre das etwas anderes, das geht aber nicht weil man für die vollständige Abbildung der reellen Zahlen unendlich viele Stellen nach dem Komma abbilden müßte.

Oder anders gesagt, Dein Programm verhält sich korrekt wenn es nie "Habs" ausgibt. Würde es das auf einmal tuen dann wäre dies ein Fehler. Umgekehrt wäre der einzig richtige Weg um hier vorzugehen entweder der Epsylon-Ansatz oder ein anderer Datentyp als double.

Btw, Die Ungenauigkeit von double ist genaugenommen kein Fehler sondern liegt in der Natur des Formats. Es ist der Preis den man dafür zahlt das man einen sehr großen Zahlenbreich damit abdecken kann. Das gleiche gilt auch z.B. für int. Rechne mal int x = 3 / 2; da kommt garantiert auch nicht das korrekte Ergebnis raus...
 
Zurück