Sinus von Hand berechnen, Probleme beim realisieren



  • Taurin du wolltest doch noch den Code posten 🙂



  • double my_sin(double x)
    {
       // sin(x) = summe von k = 0 bis undenlich[pow(-1,k)*pow(x,2*k+1)/fak(2*k+1)] 
       double fak_counter = 1, zaehler, nenner = 1, res = 0, res_old;
    
       double vz = 1, x_ohne_nachkomma;
    
       if(x < 0) // nur positive Argumente: sin(-x) = -sin(x)
       {         // x ist jetzt positiv
          x   = -x;
          vz *= -1;
       }
       if(x >= 2*pi) // der Sinus ist 2-pi-Peridisch: sin(x) = sin(x + 2*k*pi)
       {             // x ist jetzt kleiner als 2*pi
          x_ohne_nachkomma = floor(x / (2*pi)); /* Nachkomma abschneiden ! */
          x -= 2*pi*x_ohne_nachkomma; 
       }
       if(pi < x && x < 2*pi) // da sin(x) = -sin(2*pi - x) bringen wir das
       {                      // x auf das Intervall [0,pi] 
          x   = 2*pi - x;
          vz *= -1; 
       }
       if(pi/2 < x && x < pi) // da sin(x) = sin(pi - x) kommen wir auf das
       {                      // Intervall [0, pi/2]
          x = pi - x; 
       }
    
       zaehler = x;
    
       do
       {      
           res_old      = res;   
           res         += zaehler / nenner;
           zaehler     *= -1 * x * x;
           fak_counter += 2;
           nenner      *= (fak_counter-1) * fak_counter;
       }while(res != res_old); // wenn res == res_old trägt der nächste Summand
                               // nichts mehr zum Ergebnis bei
    
       return vz * res;
    }
    


  • Die If-Bedingungen über der Schleife sind nur Optimierungen für bestimme Fälle
    wenn ich das richtig verstanden habe und das in der do-while Scheife dann der
    eigentliche Code zur Berechnung des Sinuswertes, right?



  • Die if-Bedingungen dienen dazu, x auf das Intervall [0, pi/2] zu schieben
    (und das klappt auf jeden Fall. Die eizelnen Bedingungen kann man sich am
    besten am Einheitskreis klar machen).
    Das kann, je nach ursprünglichem x, einen Vorzeichenwechsel des Ergebnisses
    zur folge haben. Dafür wird das Ergebnis genauer, weil die Sinusreihe, wenn sie
    nach einer endlichen Anzahl von Thermen abgebrochen wird, bessere Werte für
    kleine x liefert.

    Die Schleife berechnet dann - wie du richtig erkannt hast - den Sinuswert
    für das neue x.



  • Ok dann ist klar 🙂



  • Das folgende geht leider nicht, nur bei 1.0 😞
    Fiel mir erst eben auf.

    // Sinus
    	public double sin(double x, int n) {
        	double result = x;
      		int ToggleSign = 0;
    
      		int i = 3;
      		do {
        		ToggleSign ^= 1;
        		result += pow(x, i)/fac(i) * ((ToggleSign == 1)? -1.0 : +1.0);
        		System.out.println ("Result ("+i+")" + result);
        		System.out.println ("Fakultät von i " + fac(i));
        		System.out.println ("Potenz von pow(x,i)" + pow(x,i));
        		System.out.println ("i " + i);
        		System.out.println ("ToggleSign " +ToggleSign);
        		System.out.println ("----");
        		i+=2;
      		}while( (--n != 0) ); // solange Anzahl nicht null ist
    
      		return result;
    	}
    

    Aber das ist doch richtig :???:



  • Mit folgendem C++ Programm geht es, Java hat keinen groß genugen Wertebereich,
    nehm ich mal an.

    #include <iostream>
    #include <cmath>
    
    using namespace std;
    
    long double fac (long double i) {
     return i > 1 ? i *= fac (i-1) : i;
    }
    
    long double sinus(double x, int n) {
    	long double result = x;
      	int ToggleSign = 0;
    
    	int i = 3;
    	do {
    		ToggleSign ^= 1;
        		result += pow(x, i)/fac(i) * ((ToggleSign == 1) ? -1.0 : +1.0);
        		i+=2;
      	}while( (--n != 0) ); // solange Anzahl nicht null ist
    
    	return result;
    }
    
    int main () {
    
     long double eingabe;
     cin >> eingabe;
    
     cout << "sinus eigene " << sinus (eingabe, 8) << endl;
     cout << "sinus lib " << sin (eingabe) << endl;
     cin >> eingabe;
     return 0;
    }
    


  • Sind jetzt leider 4Postings von mir in Folge,aber durchs editieren würde es nur
    unübersichtlich werden.
    Konnte in der Schule nicht in Ruhe posten, jetzt nochmal alles ganz ausfürhlich.

    Das Programm funktioniert mit C++ perfekt, auch ohne long double, ein einfacher
    double genügt, unter Java erhalte ich jedoch nach wenigen Iterationen über die
    fakultät und die pow-Funktion ein "infinity", c++ packt auch noch 8 für n bei
    der sin-funktion, Java nichtmal 4 wenn ich den Sinus von 2 wissen will.
    Die Ergebnisse des Programmes stimmen bei C++ exakt mit denen der cmath sinus-funktion
    überein.
    Und denen der Sinus-Funktion der Java Math-Lib.

    Das Problem ist also etwas Java-spezifisches, das ich nicht kenne (Operatorreihenfolge kann es eigentlich nicht sein, habe schon einklammern versucht).
    Da die Wertebereiche gleich sind ist mir das "infinity" in Java was in C++
    ausbleibt (würde der Wertebereich überschritten, könnte das Ergebnis ja nicht
    mehr stimmen).

    Ist hier zufällig ein Java-Experte unter uns der auch C++ kann?
    Vllt. kann man den Thread ja auch ins Java-Forum verschieben, falls mir hier keiner
    helfen kann.



  • Verwende unter Java doch einfach mal die Klasse BigDecimal.



  • Habe das ganze jetzt mal versucht so zu lösen, aber das Problem ist wohl, dass ich
    für BigDecimal einen double zum konstruiren brauche, bekomme hier ebenfalls
    falsche Ergebnisse.
    Verstehe das aber nicht, double ist in c++ und java doch gleich groß 😕

    public BigDecimal sin(double x, int n) {
        	BigDecimal result = new BigDecimal (x);
      		int ToggleSign = 0;
    
    		int i = 3;
    		do {
    			ToggleSign ^= 1;
        		result.add (new BigDecimal(pow(x, i)/fac(i) * ((ToggleSign == 1) ? -1.0 : +1.0)));
        		i+=2;
      		}while( (--n != 0) ); // solange Anzahl nicht null ist
    
    		return result;
    	}
    

Anmelden zum Antworten