Java Rechenfehler aufgrund zu geringer Zahlendichte.

Maggiefix

Ensign
Registriert
Juli 2008
Beiträge
226
Hallo CB,

ich habe neben meinem Hauptstudiengang ein Infomodul belegt, weil es mich interessiert. Wir beschäftigen uns dort mit Java und ich bin nun vor folgendes Problem gestellt.

Aufgabe

Gegeben sei in einem Programm Sinus.java eine Klassenmethode mySin zur Berechnung der
Funktion sin(x) mit Hilfe der Reihenentwicklung auf Rechnergenauigkeit:


Code:
public static double mySin( double x)
{
double summeNeu, summeAlt, summand;
int i = 1;
summand = summeNeu = x;
do
{
summeAlt = summeNeu;
i++; summand = - summand * x * x / i;
i++; summand = summand / i;
summeNeu = summeAlt + summand;
} while( summeNeu != summeAlt);
return summeNeu;
}

b) Für sehr große Beträge von x werden die Ergebnisse auf Grund der geringeren Zahlendichte in ihrer Darstellung unbrauchbar:

[table="width: 500"]
[tr]
[td]x[/td]
[td]mySin[/td]
[td]Math.sin[/td]
[/tr]
[tr]
[td]0.523599[/td]
[td]0.5000001943375614[/td]
[td]0.5000001943375613[/td]
[/tr]
[tr]
[td]3.665191[/td]
[td]-0.49999962831216327[/td]
[td]-0.49999962831216327[/td]
[/tr]
[tr]
[td]50.789081[/td]
[td]-5696.319152438529[/td]
[td]0.4999997981857671[/td]
[/tr]
[tr]
[td][/td]
[td][/td]
[td][/td]
[/tr]
[tr]
[td][/td]
[td][/td]
[td][/td]
[/tr]
[tr]
[td][/td]
[td][/td]
[td][/td]
[/tr]
[/table]


Implementieren Sie eine neue Klassenmethode mySinBesser, welche zunächst den Parame
ter auf das dichtere Intervall [0, (pi/2)] transformiert und anschließend die Methode mySin mit dem transformierten Wert aufruft.

Frage


Mir ist bewusst, dass Java maximal mit einer Genauigkeit von 64bit (double) rechnet und es deshalb zu Rechenfehlern kommen kann. Jedoch weiß ich nicht wie das mit dem Intervall gemeint ist bzw. wie ich das realisieren soll. Ich wäre wirklich dankbar, für einen Denkanstoß!
 
Ein Auto kostet 30.000€. Das sind 100% des Kaufpreises. Bezahle ich bar, muss ich nur 90% des Kaufpreises hinlegen. Das wären 27.000€.
Was habe ich hier gemacht? Ich habe einen Wertebereich genommen, nämlich den in dem der Preis liegt, und habe ihn auf einen sehr viel kleineren (die Prozente) abgebildet. Ein Check mit 90% ist viel kleiner und besser als ein Koffer voller Geld.

Das ist der Gedanke, den du in deinem Programm umsetzen sollst.
 
Das Problem ist eigentlich weniger eins der Informatik und mehr eins der Numerik (Mathe).

Ich versuche es mal mit einem kleinen Denkanstoß, um nicht gleich die ganze Lösung zu verraten:
sin(0) = sin(2*pi) = sin(4*pi) = sin(6*pi) = ...

Ich hoffe, damit klärt sich auch das mit dem Intervall. Wenn nicht, bin ich auch bereit, dir dazu was zu schreiben. Wenn du die Lösung für das Problem hast, aber nicht genau weißt, warum das eine Lösung ist, erklär ich es dir auch gerne. Aber erstmal möchte ich dir zumindest die Möglichkeit geben, selbst zu überlegen. ;)
 
Okay ich danke euch für die schnelle Antwort. Da ich nicht der hellste bin in Mathematik, dauert es eine Weile bis ich das verdaut habe. Ich melde mich dann heute Abend nochmal, hoffentlich mit etwas Erkenntnis zum Thema :)
 
du hast die aufgabe nicht verstanden... die ungenauigkeit bezieht sich auf die darstellung. du sollst jetzt alle x werte in das genannte Intervall passend umwandeln..

den denkanstoss hat The_Silent_One ja schon gegeben.
 
Zuletzt bearbeitet:
Aber es ist doch schon so,
dass die eingegeben Werte zuerst umgewandelt werden müssen? Jetzt bin ich etwas verwirrt.

Die Überlegung dahinter ist doch eigentlich, dass die Kurve der Sinusfunktion periodisch ist und daher eine Betrachtung über pi/2 hinaus, nur zur einer Veränderung im Vorzeichen führt (Einheitskreis). Bin ich mit dieser Annahme auf dem richtigen Weg?
 
Lies am besten nochmal die Aufgabenstellung ganz genau:

Implementieren Sie eine neue Klassenmethode mySinBesser, welche zunächst den Parameter auf das dichtere Intervall [0, (pi/2)] transformiert und anschließend die Methode mySin mit dem transformierten Wert aufruft.


Edit:
Maggiefix schrieb:
Die Überlegung dahinter ist doch eigentlich, dass die Kurve der Sinusfunktion periodisch ist und daher eine Betrachtung über pi/2 hinaus, nur zur einer Veränderung im Vorzeichen führt (Einheitskreis). Bin ich mit dieser Annahme auf dem richtigen Weg?
Genau
 
Maggiefix schrieb:
[table="width: 500"]
[tr]
[td]x[/td]
[td]mySin[/td]
[td]Math.sin[/td]
[/tr]
[tr]
[td]0.523599[/td]
[td]0.5000001943375614[/td]
[td]0.5000001943375613[/td]
[/tr]
[tr]
[td]3.665191[/td]
[td]-0.49999962831216327[/td]
[td]-0.49999962831216327[/td]
[/tr]
[tr]
[td]50.789081[/td]
[td]-5696.319152438529[/td]
[td]0.4999997981857671[/td]
[/tr]
[tr]
[td][/td]
[td][/td]
[td][/td]
[/tr]
[tr]
[td][/td]
[td][/td]
[td][/td]
[/tr]
[tr]
[td][/td]
[td][/td]
[td][/td]
[/tr]
[/table]

Aber wenn ich mir hier den ersten Wert anschaue, liegt dieser ja bereits im Intervall [0 ; pi/2] und dennoch wird nicht genau gerechnet?

Könntet ihr mir nicht vielleicht ein kleines Beispiel geben, ich steh gerade echt auf dem Schlauch.
 
Zuletzt bearbeitet:
Ähm, doch? Die Ergebnisse der ersten Eingabe sind doch äquivalent? Bzw. sin(0.523599) ≈ 0.5 stimmt doch?
 
Zuletzt bearbeitet:
achte nicht so sehr auf die hinteren nachkommastellen. auch bei math.sin sind es nur annäherungen an den tatsächlichen wert. dein vorgegebener algorithmus zur berechnung vom sin ist genau genug bei den kleinen werten von x. ;)
 
Okay sagen wir ich hätte den Wert 6,3pi wie transformiere ich diesen in den Intervall [0; pi/2] ?
Ich stelle mir vor, dass man X jetzt einfach in den entsprechenden Intervall schiebt, aber halt so, dass sich das Ergebnis nicht verändert.
 
nunja. denken wir mal mathematisch:

sin(x) = sin(x + 2pi) = sin(x + 4pi) = sin(x + 6pi) = ... [ = sin(x + k*2pi) für jede ganze zahl k]
hier kann mal also 2pi so oft von x abziehen (stichwort modulo), bis x zwischen 0 und 2pi liegt (vorausgesetzt, x ist nicht negativ)

außerdem gilt:
sin(x) = -sin(-x)

wenn du die beiden tatsachen geschickt zusammen benutzt, solltest du dein x so ändern können, dass es zwischen 0 und pi liegt und das ergebnis trotzdem das gleiche bleibt.
 
Noch eine kleine Anmerkung:
Du musst die Eingabe ja in das Intervall [0;(pi/2)] abbilden und nicht [0;pi], richtig ?

Das machte es nämlich noch etwas komplizierter und mit den Regeln von @The_Silent_One kommt man noch nicht ganz hin. z.B. was macht man mit x=pi ?

Du musst dir also noch einen weiteren Trick überlegen. Schau dir mal die Sinuskurve an. Was man dort relativ einfach sehen kann, ist folgendes: sin(pi/2+y) = sin(pi/2-y) , was für alle y gilt.

Wenn Du also die Regeln von @The_Silent_One angewendet hast, dein x aber immer noch größer als (pi/2) ist, musst du anschließend noch die eben genannte Regel anwenden.
 
Zuletzt bearbeitet:
So ich musste mich leider erstmal mit anderen Aufgaben beschäftigen, deshalb habe ich nicht antworten können.

Das habe ich bisher:
Code:
public class mySin {
	
	public static void main(String[] args) {
		
		System.out.println(mySin(trans(5)));
		
	}
	
	
	
	// Transformieren des X-Wertes
	public static double trans(double x) {
		while ( x > (0.5 * Math.PI)) {
			x = x - (2* Math.PI);
		}
		
		return x;
	}
	

      // Berechnen von sin(x) 
	public static double mySin( double x)
	{
	double summeNeu, summeAlt, summand;
	int i = 1;
	summand = summeNeu = x;
	do
	{
	summeAlt = summeNeu;
	i++; summand = - summand * x * x / i;
	i++; summand = summand / i;
	summeNeu = summeAlt + summand;
	} while( summeNeu != summeAlt);
	return summeNeu;
	}
	
}}

Jedoch so wie ich die Aufgabe verstanden habe muss ja der gelieferte X-Wert zwischen 0 und pi/2 liegen er darf also nicht kleiner 0 sein. Das bekomme ich aber beim besten Willen einfach nicht hin, jedenfalls nicht ohne den Code der Funktion mySin zu verändern.
 
Zuletzt bearbeitet:
Du musst mehrere unterscheidungen machen
1. wenn x > 2*pi => x = x-2*pi weil z.B. sin(0) = sin (2*pi) das ist die offensichtliche
2. wenn pi > x > 2*pi => sin(x)= -sin(x-pi) das heist du kannst als erstes x-pi rechnen und dann das ergebnis negieren
3. wenn pi/2 > x > pi => sin(x)=sin(x-pi/2) das heist du kannst vorher x-pi/2 rechnen
4. negative werte => sin(-x)=-sin(x) danach musst du nochmal überprüfen ob x jetzt zu einen der fälle 1-3 passt

ich hoff mal des hatt dir die mathematik hinter dem ganzen erklärt. Kann man alles sehen wenn man sich die sinuskurve mal aufmahlt und anschaut
du kannst quasi jeden wert von x so umrechnen das er zwischen 0 und pi/2 liegt und trotsdem noch das richtige ergebnis rausbekommen wenn du danach noch andere berechnungen machst

ich hoff ich konnte es dir erklären und hab dich nicht nur verwirrt
du musst halt vorher unterscheiden um welchen fall es sich handelt
 
Muss das bei 2. nicht: pi < x < 2*pi => sin(x)= -sin(x-pi) heißen?
und bei 4: pi/2 < x < pi => sin(x)=sin(x-pi/2)

Und welches Schleifenkonstrukt würde sich dafür anbieten? Eine do - while mit untergrordnenten while und if Schleifen ?
 
Zuletzt bearbeitet:
Alle Schleifen sind gleichwertig und können ineinander umgewandelt werden. Ich zähle hier die Rekursion als eine besondere Art einer Schleife mit ein.
Du kannst also verwenden, was du willst.

Btw, if ist kein Schleifenkonstrukt.
 
ich würde es ungefähr so machen (ich schreibs nicht als code sonst hast du ja keine arbeit mehr und weil ich zu faul bin ^^):

neg=1
1.if (x<0) x = -x & neg=-1
2.while(x>2*pi) x = x-2*pi
3.if (pi < x < 2*pi) x = x-pi &neg=-1
4.if (pi/2<x<pi)x = x -pi/2

x = x*neg

und dann kannst du deine sinusfunktion laufen lassen
ich hoff mal ich hab keine fehler reingebaut aber ich hab grad wenig zeit
schaust halt mal
 
Zuletzt bearbeitet:
Mir ist schon klar das ich nehmen kann was ich will, ich würde nur gerne eine Empfehlung haben wie ich das möglichst schnell und einfach lösen kann.
Das Modul wird mir zwar angeboten, ist aber leider komplett auf Informatiker zugeschnitten. Das bedeutet mir fehlen die ganzen anderen Module wie Mathematik und was man da sonst noch so hat als Grundlage.
 
Zuletzt bearbeitet:
Zurück
Oben