float-Krampf



  • Hallo!

    float macht mir zu schaffen! Ich mache ein Konsolenspiel in dem Objekte bewegt werden. Da dies aber nicht zu schnell passieren soll, wird die Objektposition in float abgespeichert. Vor dem Setzen des Objekts wird nach int gecastet.

    Nehmen wir 2 Objekte als Beispiel. Nun wird zB. deren Position Y jeden Tick() um 0.25f erhöht.
    Doch schon nach wenigen Ticks ist ein Objekt weiter als das andere! Dabei sollten die Schritte doch 0.25, 0.5, 0.75, 1.0, 1.25 usw. sein. Sind sie aber anscheinend nicht!

    Warum ist das so? Dann müsste es ja 0.250000xxx sein, aber kann der dämliche Computer das nicht rechnen? Muss der hinten immer noch zufällige Zahlen dranhängen? Geht das nicht sauber?

    Habe mir dann überlegt für die Position doch int zu nehmen und halt zu dividieren, also zB. als Position die inkrementiert wird 800, aber die eigentliche Position am Bildschirm ist dann 800 / 10 also 80.

    Geht ja nur drum dass ich in einem Tick() auch mal weniger als 1 Konsolen-Kästchen rücken möchte.

    😕



  • Verkrampft schrieb:

    Nehmen wir 2 Objekte als Beispiel. Nun wird zB. deren Position Y jeden Tick() um 0.25f erhöht.

    warum float und nicht double?

    Doch schon nach wenigen Ticks ist ein Objekt weiter als das andere! Dabei sollten die Schritte doch 0.25, 0.5, 0.75, 1.0, 1.25 usw. sein. Sind sie aber anscheinend nicht!

    Google mal nach "Fließkomma Rundungsfehler"

    Warum ist das so? Dann müsste es ja 0.250000xxx sein, aber kann der dämliche Computer das nicht rechnen?

    Nein, der rechnet binär mit endlicher Genauigkeit

    Muss der hinten immer noch zufällige Zahlen dranhängen?

    die sind nicht zufällig 🙂

    Geht das nicht sauber?

    Das ist so sauber wie wenn ein Mensch mit 1/3 = 0.333333 rechnet - ohne die Periode ist das auch nur eine mehr oder weniger gute Näherung.

    Habe mir dann überlegt für die Position doch int zu nehmen und halt zu dividieren, also zB. als Position die inkrementiert wird 800, aber die eigentliche Position am Bildschirm ist dann 800 / 10 also 80.

    Das ist die bessere Lösung, ja. Wenn du dann auch noch durch eine Zweierpotenz (z.B. 8 oder 16) teilst, machst du es dem Rechner sogar noch leichter (durch zwei teilen kann er ziemlich gut und schnell...)



  • Am besten du beschäftigst dich mal mit der Binär-Representation von Gleitkommazahlen ( und auch anderen ). Dann wirst du schnell verstehen, warum nicht jede Zahl exakt dargestellt werden kann. Es sei denn du stellst unendlich viele Bits zur Verfügung 🙂



  • mach das mit ganzzahlen, long oder so. in der konsole gibt es ja auch keine 0.1 buchstabenzelle. da das nicht so schnell passieren soll ist kein argument, den unterschied merkste kaum, dafür gibt es funktionen wie sleep, etc.



  • Aber wenn immer um 0.25 erhöht wird, sollten doch keine weiteren Werte dahinter entstehen?! Warum sollte zB. 0.0f + 0.25f = 0.250XXX sein?

    Hab das jetzt mal getestet:

    float x = 0.0f;
    
    for(int i = 0; i < 1000; ++i)
    {
    	x += 0.25f;
    	cout << x << endl;
    }
    

    Am Ende kommt aber 250 raus. Jedoch in meinem eigentlichen Programm mache ich auch nix anderes als immer 0.25f dazuzuzählen... und trotzdem verändern sich die Abstände der Objekte. Oder liegt das hier an cout?

    @rund0r
    Ich arbeite nicht mit Sleep(), aber vielleicht sollte ich einen extra Timer für die Anzeige verwenden?

    Hmm...



  • Ha, gleicher Code, allerdings 200 Schritte mit 0.275f Erhöhung:

    Alles gut bis 46.75f. Dann plötzlich im nächsten Schritt ist der Wert 47.0251.

    😕



  • Verkrampft schrieb:

    Alles gut bis 46.75f. Dann plötzlich im nächsten Schritt ist der Wert 47.0251.

    Das liegt daran, dass sich 0.275 nicht exakt als Fließkommawert darstellen lässt. Der Computer arbeitet mit einem Näherungswert.

    0.25 sollte allerdings passen. Wenn das bei dir nicht der Fall ist, machst du noch etwas anderes falsch.



  • Ja stimmt, ich habe gerade einen Fehler beseitigt welcher das Problem anscheinend gelöst hat (Ich habe die Objekte nicht 'synchron' auf Position 0 zurückgesetzt, also einfach x = 0 geschrieben (Abstand nicht mehr genau 50) anstatt zB. x -= 50 (Abstand genau 50)).

    Danke jedenfalls!



  • Verkrampft schrieb:

    0.0f + 0.25f = 0.250XXX sein?

    Weil 0.25f binär nicht exakt darstellbar ist. Der Compiler übersetzt das ins Binäre, rechnet damit und übersetzt es dann wieder ins Dezimale für die Ausgabe. Bei beiden Übersetzungen kommen Rundungsfehler, die sich eben nicht aufheben.



  • pumuckl schrieb:

    Weil 0.25f binär nicht exakt darstellbar ist.

    0.25f ist binär exakt darstellbar.


Anmelden zum Antworten