Sinus von Hand berechnen, Probleme beim realisieren
-
Ne, unsere Postings haben sich überschnitten so hab ich deines nicht gesehen.
Die Funktionsweiße ist mir fast klar, nur eines versteh ich gerade nicht wie
funktioniert dein Vorzeichenflag genau?
Am Anfang ist es false dann wird es mit ^= 1 zu true und danach wieder zu false?Ich mag Java nicht, da funktioniert das hin und her schalten von true und false
nicht. Wie kann ich es ändern dass es weiterhin funktioniert? Geht es mit int?Habe das jetzt an Java angepasst, alledings funktioniert es nicht (Ergebnis ist viel zu groß):
public double sin(double x, int n) { double result = x; int ToggleSign = 0; int i = 3; do { bToggleSign ^= 1; result += pow(x, i)/fac(i) * ToggleSign == 1? -1 : +1; i+=2; }while( --n > 0 ); // solange Gliedanzahl nicht null ist return result; }
Ich denke mal dass ^= 1 für den int nicht so wie für bool in c++ wirkt.
Also brauch ich wohl ne andere Möglichkeit das Vorzeichen zu testen, nur wie?
-
Am Anfang ist es false dann wird es mit ^= 1 zu true und danach wieder zu false?
Ja, auf diese Weise kann man Bits elegant "toggeln". Das kannst du eigentlich mit jeder Variable machen, sogar mit Zeigern; mit "^1" wird ja schließlich immer nur das erste bit der Variable ge-XORed.
Du hast noch Fehler in deiner Funktion (die while-Bedingung), aber theoretisch müsste sie funktionieren...
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 : +1; i+=2; }while( --n != 0 ); // solange Gliedanzahl nicht null ist return result; }
-
Auch deine Version liefert die gleichen falschen werte
-
Diese Funktion habe ich jetzt selber ausprobiert. Der Ternary-Operator ist anscheinend schwächer als das Mal, deshalb habe ich es umklammert. Statt mit -1 bzw. +1 zu multiplizieren, multipliziere ich jetzt mit -1.0 bzw. +1.0, somit erzwingen wir eine floating point multiplikation.
Für n darfst du nicht so große Zahlen angeben, weil das Resultat sonst ziemlich schnell in die Höhe schnellt (die Funktion fac() sollte ein double zurückgeben, dann kannst du auch etwas größere Zahlen eingeben..).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.0 : +1.0); i+=2; }while( (--n) ); // solange Gliedanzahl nicht null ist return result; }
-
Das Ergebnis:
deines: 0.841468253968254
li: 0.8414709848078965
Also lags tatsächlich an dem Trinären Operator.Kannst du mir deine Funktion mal kurz erklären, muss die Optimierungen vor dir
nacher auch meinem Lehrer erklären können.
-
Ich gebe noch mal ein paar Dinge zu bedenken:
(1) Ihr rechnet die Potenzen / Fakultäten jedes mal wieder neu aus.
Wenn ihr den Sinus genau ausrechnen wollt, kostet das schnell viel
Rechenzeit. Das geht auch schlauer(2) Wenn man sich noch zwei Gedanken über Fließkommaarithmetik macht,
bekommt man ne hübschere Abbruchbedingung für die Schleife: Wie lange
muss man rechnen, damit der neu berechnete Therm nichts mehr zum Ergebnis
beiträgt? Dann könnt ihr euch das n sparen.(3) Die Sinusreihe (so heißt die Formel) gibt nur gute Ergebnisse für Werte
nahe x = 0. Wie kann man die Periodizität des Sinus ausnutzen, um auch für
x = 3000*pi das richtige Ergebnis zu bekommen?Wenn du deinen Lehrer beeindrucken willst, kann ich mal was entsprechendes posten
-
SirLant schrieb:
Kannst du mir deine Funktion mal kurz erklären, muss die Optimierungen vor dir
nacher auch meinem Lehrer erklären können.int ToggleSign = 0; //.. ToggleSign ^= 1;
kannst du (hier) durch
bool ToggleSign = false; //.. ToggleSign = !ToggleSign;
ersetzten. Wirds klarer?
-
Gerne,auch wenn mein jetztiges für den Lehrer bereits genug ist, aber für mich nicht
mach aber bitte ein paar Erklärungen dazu, damit ich es auch verstehe
-
Die Stelle ist mir klar, mir wurd jetzt eigentlich alles klar, da das i ja zu Beginn
3ist und danach einfach in 2er Schritten hochgezählt wird
-
(1) Ihr rechnet die Potenzen / Fakultäten jedes mal wieder neu aus.
Wenn ihr den Sinus genau ausrechnen wollt, kostet das schnell viel
Rechenzeit. Das geht auch schlauer(2) Wenn man sich noch zwei Gedanken über Fließkommaarithmetik macht,
bekommt man ne hübschere Abbruchbedingung für die Schleife: Wie lange
muss man rechnen, damit der neu berechnete Therm nichts mehr zum Ergebnis
beiträgt? Dann könnt ihr euch das n sparen.(3) Die Sinusreihe (so heißt die Formel) gibt nur gute Ergebnisse für Werte
nahe x = 0. Wie kann man die Periodizität des Sinus ausnutzen, um auch für
x = 3000*pi das richtige Ergebnis zu bekommen?1. Guter Tipp.
2. Das n ist ein Argument der Funktion, und keine neue Variable, daher denke ich, dass es elegant gelöst ist.
3. Hab ich nicht ganz verstanden.
-
Das n stand auf dem Arbeitsblatt, aber im normalfall rundet man danach ja eher bei bedarf
-
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.