floor(double) Funktion funktioniert nicht!



  • a = 1.0 / ( 3.2 - 3.0 ) = 5.0

    cout << a << endl
    

    gibt ja auch 5 aus! Also sollte floor(a) 5 zurückgeben, oder? Und nicht 4! floor(5.0) liefert mir ja auch 5.0!

    Ich versteh das einfach nicht wieso sich die Funktion mal so und mal so verhält, was ist falsch an meinem Code?



  • Real_PsychoDAD schrieb:

    a = 1.0 / ( 3.2 - 3.0 ) = 5.0

    Irrtum. Du rechnest mit doubles nicht mit reellen Zahlen. Aufgrund der nicht beliebigen Genauigkeit von doubles kommt da irgendwas < 5.0 raus. floor liefert dann korrekterweise 4.0.

    Zum Thema double-Genauigkeit sollte dir die Suchfunktion weiterhelfen.





  • floor(a+0.5);
    

    HTH



  • @kingruedi: Jetzt rundest Du aber nicht mehr ab. Sondern Du rundest ganz normal. Damit behebst Du das grundsätzliche Problem nicht.



  • Und warum gibt mir

    cout << a << endl
    

    5 aus und nicht 4.99999 oder sowas?



  • Weil es auf ein paar Stellen rundet.



  • Hi,

    dass der Wert 5 angezeigt wird, kann von sonst was abhängen, Fakt ist, dass der in double gespeicherte Wert nicht genau als 5 dargestellt werden kann und somit ein wenig drüber oder drunter sein muss. Hier ist das eben drunter (immer, k.A.).
    Du solltest dir anschauen, wie Gleitkommazahlen gespeichert werden.

    MfG Eisflamme


  • Mod

    5 kann durchaus exakt dargestellt werden, aber nicht 3.2; das wäre nähmlich
    11.0011211.\overline{0011}_2
    interessant ist das problem insofern, als double 53 signifikante stellen hat, wobei die führende 1 nicht gespeichert wird, folglich sollte die darstellung nach 2 einsen abbrechen, da eine 0 folgt wird nicht aufgerundet - also ist die gespeicherte zahl tatsächlich etwas kleiner als 3.2
    folglich müsste als nächstes etwas herauskommen das auf keinen fall kleiner als 5 ist (weil 5 exakt dargestellt werden kann) - es könnte daran liegen, dass intern im double extended format gerechnet wird, oder aber an bestimmten 'optimierungen' des compilers. die semantik von gleitkommaoperationen ist eben ein kapitel für sich 🙂



  • Okay, das ist aber nicht schön ...

    Also konkret brauch ich einen Algorithmus zum berechnen von Kettenbrüchen.

    unsigned continuedFraction( unsigned* values, unsigned n, double val )
    {
     double c = abs(val); 
     int    i = 0;
     while( i < n )
     {
      double f = floor(c);
    
      values[i] = f;
      c = 1.0 / ( c - f );
      ++i;
     }
     return i;
    }
    

    Das war mein bisheriger Algorithmus, ich wusste, dass dieser Algorithmus ab einer bestimmten "Iterationstiefe" versagt(z.B. bei 3.1415926536) hat er in den hinteren Stellen ( ~ grösser 10 ) falsche Werte geliefert), das hing auch mit der Gleitpunktungenauigkeit zusammen. Das war aber akzeptabel, solange die ersten 5 Stellen korrekt waren, genauer brauchte ich es nicht. Nun habe ich mit diesem Algorithmus versucht den Kettenbruch von 3.2 zu berechnen, und der ist folglich bereits in der 2 Stelle falsch! Das ist nicht akzeptabel. Der Kettenbruch von 3.2 ist [3,5] und nicht [3,4,1] wie mein Algorithmus ihn errechnet, wenn ich damit rechnen muss dass mein Algorithmus mir solch krass falschen Ergebnisse liefert, dann ist er praktisch wertlos.

    Also vielleicht kennt hier jemand einen besseren Algorithmus zur Berechnung von Kettenbrüchen, der wenigstens die ersten 5 Werte richtig errechnet?

    Oder wie kann ich diesen Algorithmus oben modifizieren damit er läuft?
    floor(c+0.5) würde dann bei anderen Werten wieder falsche Ergebnisse liefern.


  • Mod

    ja, das ist ein interessantes problem 🙂
    nun hab ich nicht viel ahnung von nummerik, und man kann ja nicht vorsichtshalber z.b. das letzte bit immer aufrunden, denn das würde auch zu fehlern führen. eine denkbare - allerdings sehr aufwendige - korrekturmöglichkeit fällt mir ein:

    eine eigenschaft von kettenbrüchen ist ja, dass sie den grenzwert abwechselnd von oben und von unten approximieren, wobei der absolute fehler stets fällt (bis er 0 wird bei endlichen kettenbrüchen). wenn nun bei der berechnung der kettenglieder ein rundungsfehler auftritt, dann müsste eine dieser regeln verletzt sein - man könnte den exakten wert des bruchs für eine bestimmte stelle bestimmen (mittels echter bruchrechnung mit ganzzahlen) und diesen mit dem 'genauen' zielwert vergleichen und so unregelmässigkeiten aufspüren. ob das praktikabel ist, sei dahingestellt.

    im übrigen ist [3,5] dasselbe wie [3,4,1] wenn du mal drüber nachdenkst 😉

    edit: optimal gekürzte endliche kettenbrüche hören ja nie mit 1 auf (ausser [1]) - also könntest du diesen speziellen fall auch leicht nachträglich korrigieren.


Anmelden zum Antworten