Problem mit fmod Funktion



  • Hallo zusammen. Ich habe ein riesen Problem mit folgendem Codebeispiel und zwar kommt er regelmässig in folgenden Codeabschnitt rein: AAHHH LOOOORD

    Aber wie kann das sein? Wie kann der Ramainder einer Modulation grösser sein als der Divisor?

    Vector2 vcSmp;
    
     float32 sfh = 100000.0f;
    
     vcSmp.X = fmod(sfh/2.0f+15.0f*X+this->fSed*1000.0f,sfh)-sfh/2.0f;
     vcSmp.Y = fmod(sfh/2.0f+15.0f*Z,sfh)-sfh/2.0f;
    
     if(vcSmp.X < -sfh/2.0f || vcSmp.Y < -sfh/2.0f || vcSmp.X > sfh/2.0f || vcSmp.Y > sfh/2.0f){
      // AAAHHH LOOOOOOOOORD
      int i = 1;
      i = i;
     }
    


  • Schreib doch bitte mal ein reproduzierbares Beispiel, bei dem man nicht raten muss, was z.B. this->fSed ist, etc.

    Gruß,
    B.B.



  • Ishildur schrieb:

    Aber wie kann das sein? Wie kann der Ramainder einer Modulation grösser sein als der Divisor?

    Das ist leicht. Versuch's mal so:

    #include <stdio.h>
    #include <math.h>
    
    int main(void)
    {
        double a, b;
    
        a = 10;
        b = -3;
    
        printf("dividend:   %.0lf\n"
               "divisor:    %.0lf\n"
               "remainder:  %.0lf\n",
               a, b, fmod(a, b));
    }
    

    🙂



  • OK, I see I see...

    Hmmm...
    Was ich suche ist eine Funktion mit folgender Funktionsvorschrift:

    X vcSmp.X
    -------------------
    -50'000 -> -50'000
    0 -> 0
    50'000 -> 50'000
    -50'001 -> 49'999
    50'001 -> -49'999

    Dazu kommt noch die Schwierigkeit, dass X mit einem beliebigen Faktor (in diesem Fall 15) multipliert werden kann, sowie ein Seed dazu addiert wird. Ach ja this->fSed ist einfach ein 32bit float Wert, welcher die Anzahl vergangener Sekunden seit begin der Animation beschreibt...

    Ich bin nicht so das Genie in der diskreten Mathematik und finde einfach keine passende Formel, welche diese Funktionsvorschrift erfüllt... 😞



  • Soll sich die Funktion so verhalten?

    #include <stdio.h>
    #include <math.h>
    
    double f(double x)
    {
        return fabs(x) > 50000 ? -x : x;
    }
    
    #define tell(var) printf("f(%.0lf)\t-> %.0lf\n", var, f(var))
    
    int main(void)
    {
        tell(-50000.0);
        tell(0.0);
        tell(50000.0);
        tell(-50001.0);
        tell(50001.0);
    }
    

    Nur mal um zu sehen, ob ich dich richtig verstanden habe.
    🙂



  • @mngbd
    Hehe, ganz so einfach ist es leider nicht 😉 Ich benötige ein Ring, dass bedeutet, dass sich die Zahlenfolge nach einem bestimmten Wert wiederhohlt. Stell dir ein Intervall auf einem Zahlenstrahl von -50'000.0f bis 50'000.0f vor. Sobald dieses Intervall auf der einen Seite verlassen wird, soll auf der anderen Seite wieder hineingesprungen werden. Der von dir gezeigte Code macht folgendes:

    -50'000 -> -50'000
    0 -> 0
    50'000 -> 50'000
    -50'001 -> 50'001 // hier sollte jedoch 49'999 kommen
    50'001 -> -50'001 // hier sollte jedoch -49'999 kommen



  • 🕶 Huaaaaaaayy ! 🕶

    Ist das nicht einfach nur die eine oder andere if Abfrage? 😕

    #define LIMIT 50000.0
    
    double my_func ( double number )
    {
    	double rest = fmod ( number, LIMIT );
    	if ( rest < 0 )
    		return 50000 + rest;
    	if ( rest > 0 )
    		return -50000 + rest;
    	return number;
    }
    
    int main()
    {	
    	printf ( "%G\n", my_func( -50000 ) );
    	printf ( "%G\n", my_func( +50000 ) );
    	printf ( "%G\n", my_func( -50001 ) );
    	printf ( "%G\n", my_func( +50001 ) );
     	return 0;
    }
    

    Gruß,
    B.B.



  • Ishildur schrieb:

    @mngbd
    Hehe, ganz so einfach ist es leider nicht 😉

    Huh, so einen Blödsinn bemerke ich sonst meistens bevor ich ihn schreibe... 🙄

    Jedenfalls hab ich's jetzt verstanden. Etwa so:

    #define LOW (-50000.0)
    #define HIGH (50000.0)
    
    double f(double x)
    {
        return (x < 0 ? HIGH : LOW) + fmod(x, HIGH);
    }
    

    Das hat nur noch den Nachteil, dass es bei 0 nicht stimmt. Mal grübeln...
    🙂



  • @mngbd und Big Brother
    Vielen Dank für eure Beiträge, leider funktionieren beide Varianten nicht (IMHO sind auch beide dieselben, nur ein wenig anders geschrieben 😉

    Bspw:

    25000.0f mappt auf -25000.0f, richtig wäre aber 25000.0f -> 25000.0f...

    Vielleicht möchte ein Moderator diesen Thread ins Mathematik Forum verschieben? Ich hatte ursprünglich in dieses Forum gepostet, weil ich dachte es wäre ein Problem mit der fmodf Funktion, nun scheint es mir jedoch ein rein mathematisches Problem zu sein



  • Ich versuche das Problem ein wenig genereller zu formulieren:

    f(-2.0f) = 0.0f
    f(-1.5f) = 0.5f
    f(-1.1f) = 0.9f
    f(-1.0f) = -1.0f
    f(-0.5f) = -0.5f
    f(0.0f) = 0.0f
    f(0.5f) = 0.5f
    f(1.0f) = 1.0f
    f(1.1f) = -0.9f
    f(1.5) = -0.5f
    f(2.0f = 0.0f

    usw....



  • Ishildur schrieb:

    Vielleicht möchte ein Moderator diesen Thread ins Mathematik Forum verschieben? Ich hatte ursprünglich in dieses Forum gepostet, weil ich dachte es wäre ein Problem mit der fmodf Funktion, nun scheint es mir jedoch ein rein mathematisches Problem zu sein

    Ich empfehle einen neuen Thread, da liegt die psychologische Einstiegsschwelle geringer. Dort wäre das sicher besser aufgehoben, offenbar haben wir wenig Ahnung von Modularithmetik. Kannst ja einen Link hierher setzen.

    Eigentlich sollte dein Problem schon sehr oft aufgetaucht sein. Ob man da was schlaues nachschlagen kann...?
    🙂



  • Ishildur schrieb:

    Stell dir ein Intervall auf einem Zahlenstrahl von -50'000.0f bis 50'000.0f vor. Sobald dieses Intervall auf der einen Seite verlassen wird, soll auf der anderen Seite wieder hineingesprungen werden.

    Ok, einen hab ich noch:

    #define LIMIT 50000.0 
    
    double my_func ( double number ) 
    { 
        double rest; 
    
     :bulb:if ( number == 0.0 || fabs ( number ) <= LIMIT )
    		return number; :bulb:  // Intervall wurde nicht verlassen.
    
    	rest = fmod ( number, LIMIT );
    
    	if ( rest < 0 ) 
            return 50000 + rest; 
        return -50000 + rest; 
    }
    

    🕶



  • Deine "Funktionsvorschrift" (also Wertebeispiele) macht so, wie sie angegeben ist, nicht besonders viel Sinn; dein Intervall muss an einem Ende offen sein, wenn du einen solchen Ring aufbauen willst.

    Was ich dir anbieten kann, ist

    -50.000    -> -50.000
          0    ->       0
     50.000    -> -50.000
     49.999,99 ->  49.999,99
    -50.001    ->  49.999
     50.001    -> -49.999
    

    Das sähe dann etwa so aus:

    #include <math.h>
    #include <stdio.h>
    
    double fmod_positive(double x, double y) {
      return fmod(fmod(x, y) + y, y);
    }
    
    double fmod_range(double x, double min, double max) {
      return fmod_positive(x - min, max - min) + min;
    }
    
    int main(void) {
      printf("%lf\n", fmod_range(-50000,   -50000, 50000));
      printf("%lf\n", fmod_range(     0,   -50000, 50000));
      printf("%lf\n", fmod_range( 50000,   -50000, 50000));
      printf("%lf\n", fmod_range(49999.99, -50000, 50000));
      printf("%lf\n", fmod_range(-50001,   -50000, 50000));
      printf("%lf\n", fmod_range( 50001,   -50000, 50000));
    
      return 0;
    }
    


  • Die Lösung ist...

    double min = -50000;
    double max =  50000;
    double differenz = max - min;
    
    double rest = fmod(dividend - min + differenz, differenz) + min;
    

    Kurz:

    double rest = fmod(dividend - 2 * min + max, max - min) + min;
    

    Sinn der Sache:
    Durch dividend - min wird der negative minimalwert ausgeglichen. Moduliert wird durch die differenz von min und max und dann min danach wieder hinzu gerechnet. Dadurch ist das minimal mögliche Ergebnis min und das maximal mögliche Ergebnis differenz + min - als max.

    Die Differenz vor dem Modulo noch einmal zu Dividend hinzuzufügen, fängt Fehler ab, die bei degativem dividend entstehen.


Anmelden zum Antworten