Problem mit "for-Schleife" und "if-Anweisung"



  • Hallo, was das Programmieren mit C++ angeht bin ich noch ziemlich neu in der Materie. Komme aber so weit ganz gut zurecht. Nun habe ich ein Problem mit einer Schleife, das ich nicht gelöst bekomme, obwohl ich weiß, dass es nicht so schwer sein kann.

    Zunächst schildere ich mal, was das Programm machen soll.
    Zunächst habe ich mal ein kleines Programm geschrieben um mein Problem einzeln zu Testen. Alles was jetzt gemacht werden soll, ist die Variable t in jedem Schleifendurchgang um +0.1 zu erhöhen. Dazu habe ich die Schleife:

    void Rechne()
    {
      for (; t<10.15; t=t+0.1)
      Ausgabe();
    }
    

    In der Ausgabe soll der Wert t ausgegeben werden. Allerdings sollen nur die ganzen Zahlen, also 1,2,3,4... ausgegeben werden. Habe mir nun gedacht, dass man dazu eine if-Anweisung nutzen könnte. Das sieht erstmal so aus:

    void Ausgabe()
    {
      if (t == 2)
      {
        printf ("%.3f t\n", t); 
      }
    }
    

    Wollte jetzt erstmal testen, ob es möglich ist NUR den Wert t == 2 auszugeben. Später soll dann ein Zähler dahin, der immer um +1 erhöht wird, wenn die Variable t ausgegeben wurde.

    Jetzt zu meinen zwei Fragen:

    1. Warum wird t nicht ausgegeben, wenn t==2 ist. Für t>2 werden alle Werte größer 2, und für t<2 werden alle Werte kleiner 2 ausgegeben. So wie ich das gelesen habe wird für t==2 geprüft ob der linke Wert gleich dem rechten Wert ist. Wenn das doch nicht der Fall ist, dann müsste doch die if-Anweisung abgebrochen werden?

    2. Wie man in der Schleife sieht, habe ich t<10.15 gesetzt. Das ist ein Wert der eigentlich nicht erreicht werden dürfte. Wenn t nun 10.1 ist (also noch kleiner als 10.15) müsste das Programm doch noch einen Durchgang starten und der letzte ausgegeben Wert müsste 10.2 sein. Dies ist aber nicht der Fall, es wird immer bei 10.1 abgebrochen. Wieso?

    Througer



  • Dieser Thread wurde von Moderator/in volkard aus dem Forum C++ (auch C++0x) in das Forum C (C89 und C99) verschoben.

    Im Zweifelsfall bitte auch folgende Hinweise beachten:
    C/C++ Forum :: FAQ - Sonstiges :: Wohin mit meiner Frage?

    Dieses Posting wurde automatisch erzeugt.



  • Zu 1: Flieskommazahlen können nicht immer exakt dargestellt werden. Daher muss man immer davon ausgehen, dass sie mit Fehler behaftet sind. Eine Überprüfung auf Gleichheit wird somit meistens schief gehen. Du musst einen sogenannten Epsilon Test benutzen, der prüft, ob die Variable einen Wert in der Nähe von 2.0 hat.
    Also z.B so:

    if ( t >= (2.0f-epsilon) && t <= (2.0f+epsilon) )
    { /**/ }
    

    Wobei du das epsilon so wählen musst, dass für dich akzeptable Werte dabei rauskommen.

    Zu 2:
    Mach dir die Funktionsweise von einer for Schleife klar. Wenn du am Ende eines Durchganges bist, dann wird da der Schritt gemacht und dann wird geprüft. Du hast also irgendwann z.B 10.1, dann wird da 0.1 dazugezählt und dann bemerkt die Schleife, dass das ja >= 10.15 ist und bricht ab und führt den Körper nicht mehr aus.



  • Hallo, danke für die schnelle Antwort.
    Ich lese mich zur Zeit durch ein Tutorial in dem ich mir dann immer die verschiedenen Anweisungen, Befehle, etc. heraussuche. Dort ist das Beispiel:

    if (a == 10)
      printf ("a gleich 10!");
    

    Da dies ja ähnlich ist, dachte ich es könnte funktionieren.
    Aber wenn ich vorher t=2 setze und dann das Programm ausführe wird t auch ausgegeben. Mich würde jetzt noch interessieren, warum das nicht geht, nur um das auch zu verstehen. Es sollte doch nichts anderes gemacht werden, als in die if-Anweisung zu gehen und zu Prüfen ist t==2. Nein t ist erst 1.9, also wird nichts gemacht. Es wird wieder in die Schleife gegangen, dort wird t um +0.1 erhöht. Nun sind wir wieder in der if-Anweisung und es wird erneut geprüft. Diesmal ist t==2 und es wird ausgegeben. Wird dieser Vorgang übersprungen oder stelle ich mir das falsch vor?

    Das zweite Problem habe ich jetzt verstanden. Habe mir gerade nochmal das Kapital durchgelesen und hatte da einen kleinen Denkfehler. :p
    Ist natürlich klar. Es wird die 10.1 ausgegeben, dann um +0.1 erhöht. (wir sind nun bei 10.2) beim nächsten Schleifendurchgang wird geprüft welchen Wert t hat und festgestellt t>10.15, also wird der Vorgang abgebrochen.

    Nochmal danke für die schnelle Antwort.
    Througer



  • Das Problem ist, dass 0,1 binär nicht exakt darstellbar ist. Du läufst in das selbe Problem, das du hast, wenn du 1/3 dezimal genau aufschreiben willst - es hört nie auf.

    0,1 ist binär 0,000110011001100110011..., und das wird auf die verfügbare Mantisse gekürzt. Das wird bei dir vermutlich 53 binäre Stellen bedeuten (üblich für 64-bit-Fließkommazahlen), was bedeutet, dass 0,1 eigentlich (wenn ich mich nicht verrechnet habe) 0,09999999999999997779553950749686919152736663818359375 ist. (Ergebnis kann von Architektur zu Architektur und evtl. von Compiler zu Compiler variieren)

    Diese Rundungsfehler können sich in Einzelfällen wegheben, aber darauf verlassen kann man sich nicht (sofern man sich nicht auf einen speziellen Maschinentyp beschränkt und dessen Verhalten genau kennt).

    tl;dr: Fließkommazahlen sind keine genauen Datentypen. Man muss immer mit Rundungsfehlern rechnen.



  • Ach so, und da dieser Rundungsfehler besteht kann es paassieren (oder passiert in meinem Fall), dass der Wert 2 nicht exakt erreicht wird. Somit wäre die if-Anweisung nie erfüllt und es werden mir keine Werte ausgegeben.

    Danke für die Erklärung.
    Hoffe, wenn ich weitere Fragen habe, darf ich euch weiter löchern. 😃


Anmelden zum Antworten