sin() liefert sehr unterschiedliche Werte?



  • Hi

    Verzweifle grad an folgendem Code:

    #define _USE_MATH_DEFINES
    
    #include <cmath>
    
        calc_time += 0.1;
    
    	float bla = M_PI;
    
    	float a = 100 * sin( 2.0 * 3.14959 * 30 * calc_time );
    	float b = 100 * sin( 2.0 * bla     * 30 * calc_time );
    	float c = 100 * sin( 2.0 * M_PI    * 30 * calc_time );
    

    Wenn ich mir das im Debugger anschaue ist a erwartungsgemäß zwischen -100 und 100, b hingegen ist < 0.01 und c bleibt irgendwo im bereich von XXXe-10.
    IDE: VS08

    Ist bestimmt total billig aber was mache ich falsch?

    gruß

    rean



  • Wie wird calc_time denn initialisiert? Da steht quasi Schrott + 0.1 .



  • calc_time = 0.0;
    

    Wird bis auf die minimale Gleitkommaungenauigkeit auch genau wie erwartet inkrementiert.

    Das stinkt so sehr zum Himmel...hab so ein ungutes Gefühl dass das auf nen Zugriff auf ungültigen Speicher hindeutet der mir alles verhagelt.
    Warum klappt der eine Aufruf dann aber wie erwartet und die anderen nicht?
    Macht auch keinen Unterschied in welcher Reihenfolge ich die Funktionen aufrufe.



  • Schau dir doch mal an was der Präprozessor ausspuckt. Also das, was nach Ersetzug der Makros da steht und was der Compiler dann zu futtern bekommt.



  • Könntest du mir erklären wie ich das in Visual Studio 2008 mache?

    gruß

    rean



  • In den Compiler-Optionen (Rechtsklick auf Projekt -> Eigenschaften -> C++ -> Befehlszeile) noch "/P" hinzufügen (ohne die Hochkommata).



  • calc_time += 0.1;
    
    	float bla = 3.14159265358979323846;
    
    	float a = 100 * sin( 2.0 * 3.14959 * 30 * calc_time );
    	float b = 100 * sin( 2.0 * bla     * 30 * calc_time );
    	float c = 100 * sin( 2.0 * 3.14159265358979323846    * 30 * calc_time );
    

    Das ist der Code aus der main.i-Datei

    Wenn ich die c-Zeile wie folgt anpasse:

    float c = 100 * sin( 2.0 * float(M_PI)    * 30 * calc_time );
    

    Kommt wenigstens mal das selbe raus wie in der b-Zeile.
    Jetzt wirds aber richtig krank.
    Wenn ich folgendes schreibe:

    float c = 100 * sin( 30 * calc_time * 180 );
    

    Den sinus also quasi mit Winkeln füttere sind die Werte zwar nicht identisch mit der a-Zeile aber sie laufen auch von -100 bis +100.

    Alles sehr sehr seltsam...



  • rean schrieb:

    #define _USE_MATH_DEFINES
    
    #include <cmath>
    
        calc_time += 0.1;
    
    	float bla = M_PI;
    	
    	float a = 100 * sin( 2.0 * 3.14959 * 30 * calc_time );
    	float b = 100 * sin( 2.0 * bla     * 30 * calc_time );
    	float c = 100 * sin( 2.0 * M_PI    * 30 * calc_time );
    

    Wenn ich mir das im Debugger anschaue ist a erwartungsgemäß zwischen -100 und 100, b hingegen ist < 0.01 und c bleibt irgendwo im bereich von XXXe-10.
    IDE: VS08

    Ich glaube, du solltest deine Erwartungen mal mit der Mathematik abgleichen. Dein calc_time ist immer ein Vielfaches von 1/10, mit 60 multipliziert ergibt sich also eine ganze Zahl. Aber $$\sin k\pi = 0\qquad(k\in\mathbb{Z})$$, also ist ein Ergebnis von ca. 0 zu erwarten. Natürlich ist Addition von 0.1 (welches nicht exakt in floating point repräsentiert werden kann) ungenau, und die Ungenauigkeiten werden sich mit der Zeit verstärken.
    Bei c bist du am ehesten in der Nähe der Sinus-Nullstellen, bei b schon ein wenig weiter weg, weil float einfach viel weniger Stellen hat als double. Bei a allerdings steht da irgendeine Zahl, die von pi schon in der dritten Nachkommastelle abweicht, da kommt natürlich irgendwas krummes raus.



  • Auf den selben Trichter bin ich vor ein paar Minuten auch gekommen.

    Zur Erklärung:
    Wir sollen eine Membran animieren die in einem Punkt mit a*sin(2*pi*f*t) angeregt wird. Als Zeitschritte sollen wir 0.1 verwenden.

    Die 3.14959 hab ich aus dem Quelltext einer Gruppe die das letztes Jahr gemacht hat, hab mich gleich gewundert weil das so stark von pi abweicht, die haben das wohl als Workaround genommen.

    Die Frage ist wie man das Problem anständig gelöst bekommt.
    dt(Zeitschritte) so wählen dass immer 2 * dt * f < 1 gilt?



  • Da hast du ja noch einiges vor dir, bisher rechnest du gerade nur die Anregung aus (mit einer zu hohen Frequenz ... du solltest die Frequenz so niedrig wählen, dass du mehr als zwei, besser deutlich mehr, Abtastpunkte pro Periode hast). Du müsstest aber noch die partielle Differentialgleichung für die Membran aufstellen und lösen. Wahrscheinlich irgendwie in Flächenelemente endlicher Größe aufgeteilt, für eine Animation reicht das.



  • Den Part hab ich schon hinbekommen 😃

    //.h
    // lustig viele Variablen deklarieren
    
        typedef float			calc_type;
        static const GLenum		GL_TYPE = GL_FLOAT;
    
        std::vector< calc_type > vertices;
        std::vector< GLsizei > count_vec;
        std::vector< GLushort > indices;
    
        std::vector< GLushort* > indices_ptr;
    
        std::vector< calc_type > speed;
        std::vector< calc_type > accel;
    
        unsigned short x_count;
        unsigned short y_count;
    
        double calc_time;
    
        double delta_calc_time;
    
        int sampling_points_count;
    
        calc_type delta_time;
    
    //.cpp - Initialisierung
    // ein Haufen Speicher anfordern und wirr mit Pointern um die Gegend schmeißen
    
        x_count = 101;
        y_count = 101;
    
        calc_time = 0.0;
        delta_calc_time = 0.001;
    
        delta_time = 0.5;
    
        sampling_points_count = x_count * y_count;
    
        // init begin
    
        //reserve space for vertices
        // 3 cords per vertex * number of vertices
        vertices.reserve( 3 * sampling_points_count );
    
        //reserve space for acceleration and speed vector
        speed.assign( sampling_points_count, 0.0 );
        accel.assign( sampling_points_count, 0.0 );
    
        //init the count_vec
        //we want to render y_count-1 strips each one with 2 * x_count vertices
        count_vec.assign( y_count-1, 2 * x_count );
    
        //reserve space for indices_ptr
        //we want to render y_count-1 strips
        indices_ptr.reserve( y_count-1 );
    
        GLushort imax = x_count * ( y_count-1 );
    
        //reserve space for the indices
        //we need 2*(x_count+1) indices per stripe
        indices.reserve( 2 * imax );
    
        for( GLushort i = 0; i < imax; ++i )
        {
            indices.push_back( i );
            indices.push_back( i + x_count );
    
            if( ( i % x_count ) == 0 ) indices_ptr.push_back( &indices[i*2] );
        }
    
        for( int y = 0; y < y_count; ++y )
        {
            for( int x = 0; x < x_count; ++x )
            {
                vertices.push_back( x );
                vertices.push_back( 0.0 );
                vertices.push_back( -1*y );
            }
        }
    
    //.cpp - Berechnungungen
    // ein bisschen wirr in der Gegend rumindizieren
    
        int y_inc = 3 * x_count,
            y_max = 3 * sampling_points_count - y_inc,
            x_max = 3 * ( x_count-1 ),
            i_as  = 0,
            xAy;
    
        for( int y = y_inc; y < y_max; y += y_inc )
        {
            for( int x = 4; x < x_max; x+=3 )
            {
                xAy = x + y;
    
                accel[ i_as ] = vertices[ xAy + 3 ] + vertices[ xAy + y_inc ] +
                                vertices[ xAy - 3 ] + vertices[ xAy - y_inc ]
                                - 4 * vertices[ xAy ];
    
                ++i_as;
            }
        }
    
        i_as = 0;
    
        for( int y = y_inc; y < y_max; y += y_inc )
        {
            for( int x = 4; x < x_max; x+=3 )
            {
                speed[ i_as ] += delta_time * accel[ i_as ];
    
                vertices[ x + y ] += delta_time * speed[ i_as ];
    
                ++i_as;
            }
        }
    
    	double freq = 10;
    
    	const double _2pi = 2.0 * M_PI;
    
        vertices[50*3*(x_count+1)+1] = 20.0 * sin( _2pi * freq * calc_time );
    
        calc_time += delta_calc_time;
    
    //.cpp - Rendern
    //und schließlich das ganze zeichnen
    
        glEnableClientState( GL_VERTEX_ARRAY );
    
        glVertexPointer( 3, GL_TYPE, 0, &vertices[0] );
    
        glMultiDrawElementsEXT( GL_QUAD_STRIP, &count_vec[0], GL_UNSIGNED_SHORT, (const GLvoid**)&indices_ptr[0], y_count );
    
        glDisableClientState( GL_VERTEX_ARRAY );
    

    Kritik/Verbesserungsvorschläge sind herzlich willkommen 🙂


Anmelden zum Antworten