Benutzer mit den meisten Antworten
Math.Sin liefert falschen Wert!?

Frage
-
hallo zusammen.
ich habe folgendes problem:
wenn ich bei meiner anwendung einen wert in ein textfeld eingebe (als string), diesen dann zu einem double-wert umwandle, um mit ihm rechnen zu können, ihn schließlich mit math.pi/180 vom winkelmaß ins bogenmaß umrechne und diesen wert dann in math.sin einsetze komme ich zu folgenden ergebnissen:
eingabe: 0 -> ergebnis: 0 => stimmt
eingabe: 90 -> ergebnis: 1 => stimmt auch
aber:
eingabe: 180 -> ergebnis: 1,22460635382238E-16 => stimmt offensichtlich nicht, da sin(180) ja null ergibt
das selbe tritt bei einer eingabe von 360 auf, aber bei 270 wird wieder der richtige wert, also -1 angezeigt.
jetzt meine frage: wieso wird bei der eingabe von 0 (90, 270) der korrekte wert angezeigt und bei 180 bzw 360 nicht?
die werte sind zwar sehr klein, aber ich hätte trotzdem gerne, dass null angezeigt wird. kann es vielleicht an der typumwandlung von string zu double liegen?
aber wieso werden dann bei den anderen eingaben die exakten werte angezeigt?
vielleicht kennt ja einer von euch eine möglichkeit, wie man diesen problemen aus dem weg gehen kann...
ich bin für jede hilfe dankbar!
mfg
Stahl1990
Antworten
-
Hallo Stahl1990,
Gleitkommazahlen sind intern Bruchzahlen wie Du der Wikipedia entnehmen kannst -
.NET/Windows verwendet die interne IEEE Darstellung.
Daraus ergibt sich, dass eine Zahl insbesondere im Randbereich ungenau wird.
Eine Prüfung auf (Un)gleichheit mittels = (<>) schlägt deswegen schnell fehl.
.NET definiert deswegen ein Double.Epsilon (respektive Single.Epsilon für einfache Genauigkeit),
wogegen man anstatt dessen prüfen sollte. Nur gilt das für eine Operation, bei mehreren Operationen
kann sich der Fehler fortsetzen.
Da man aber in den meisten Fällen nur einige wenige (2/3) Nachkommastellen betrachtet,
fällt die Ungenauigkeit normalerweise nicht auf, da sie in der Darstellung gerundet wird.
Bei Decimal wiederum handelt es sich um eine Zahl, die im BCD System arbeitet -
zudem hat sie eine höhere Genauigkeit (28 vs. 15 Stellen bei Double).
Allerdings ist auch ein Zwitter, da mit Fließkomma gearbeitet wird:
http://www.yoda.arachsys.com/csharp/decimal.html
Zum Edit:
Bei der Anzeige reicht normalerweise: doubleValue.ToString("F3"),
Siehe Standardmäßige Zahlenformatzeichenfolgen
Gruß Elmar
- Als Antwort markiert Stahl1990 Mittwoch, 27. Januar 2010 19:19
Alle Antworten
-
Hi,
das Ergebnis 1,22460635382238E-16 entspricht ja einer Zahl, knapp über Null (0,000 000 000 000 000 12...) Der Fehler entsteht durch Rundungsfehler, da Pi schließlich eigentlich eine Unendlich lange Zahl ist. Dieser Fehler kann entstehen, wenn du die Sinus-Berechnung und die Umwandlung von Bogenmaß in Gradmaß in mehreren Zeilen durchführst (wenn du also zuerst den Bogenmaßwert berechnest und diesen in einen double speicherst, geht die Genauigkeit verloren.) Versuche es doch mal mit einer Zeile, also etwa folgendermaßen (wert entspricht deiner double-Variablen, die noch nicht umgerechnet wurde):
math.sin((math.pi/180)*wert)
Hoffe das hilft dir weiter (ist leider ungetestet)
mfg
Imperium_Romanum
MAY THE SOURCE BE WITH YOU! -
Hallo,
Gleitkommaoperationen sind systembedingt ungenau
und dem Problem kann man letztendlich nicht aus dem Weg gehen
sondern nur versuchen, den Fehler zu minimieren.
Siehe http://de.wikipedia.org/wiki/Gleitkommazahl
und etwas prosaischer im englischen Artikel
http://en.wikipedia.org/wiki/Floating_point
Gruß Elmar -
@ Imperium_Romanum:
danke für den tipp, aber leider macht die berechnung in einer zeile keinen unterschied. der wert bleibt genau der selbe...
@ Elmar:
komisch ist nur, dass dieser fehler nur bei der eingabe von 180 bzw 360 auftritt und bei den anderen eingaben (0, 90, 270) nicht. da werden dann die korrekten werte ausgegeben, wie ich ja schon geschrieben habe.
gibt es viellecht eine möglichkeit, das ergebnis auf eine bestimmte anzahl von nachkommastellen zu runden, sprich irgendeine methode dafür? das würde dann zumindest zur richtigen ausgabe führen, auch wenn dann weitere berechnungen möglicherweise ungenau werden.
[edit]
ich habe jetzt den eingegebenen string statt in double in decimal umgewandelt und dann das ergebnis der sin-funktion (auch decimal) mit der methode decimal.round() auf 10 stellen nach dem komma gerundet. somit ist die ausgabe nicht mehr 1,22460635382238E-16 sondern 0,0000000000 , was zwar wegen der vielen nullen nicht sehr schön aussieht, aber trotzdem stimmt :-)
manchmal ist es eben doch hilfreich, zuerst in der vb-dokumentation nachzuschauen...
trotzdem danke für eure hilfe...
gruß
Stahl1990
-
Hallo Stahl1990,
Gleitkommazahlen sind intern Bruchzahlen wie Du der Wikipedia entnehmen kannst -
.NET/Windows verwendet die interne IEEE Darstellung.
Daraus ergibt sich, dass eine Zahl insbesondere im Randbereich ungenau wird.
Eine Prüfung auf (Un)gleichheit mittels = (<>) schlägt deswegen schnell fehl.
.NET definiert deswegen ein Double.Epsilon (respektive Single.Epsilon für einfache Genauigkeit),
wogegen man anstatt dessen prüfen sollte. Nur gilt das für eine Operation, bei mehreren Operationen
kann sich der Fehler fortsetzen.
Da man aber in den meisten Fällen nur einige wenige (2/3) Nachkommastellen betrachtet,
fällt die Ungenauigkeit normalerweise nicht auf, da sie in der Darstellung gerundet wird.
Bei Decimal wiederum handelt es sich um eine Zahl, die im BCD System arbeitet -
zudem hat sie eine höhere Genauigkeit (28 vs. 15 Stellen bei Double).
Allerdings ist auch ein Zwitter, da mit Fließkomma gearbeitet wird:
http://www.yoda.arachsys.com/csharp/decimal.html
Zum Edit:
Bei der Anzeige reicht normalerweise: doubleValue.ToString("F3"),
Siehe Standardmäßige Zahlenformatzeichenfolgen
Gruß Elmar
- Als Antwort markiert Stahl1990 Mittwoch, 27. Januar 2010 19:19
-
Bei der Anzeige reicht normalerweise: doubleValue.ToString("F3"),
diese variante ist natürlich noch besser, da man sich damit ein paar zeilen code spart. der wert muss zur ausgabe ja sowieso in einen string umgewandelt werden.
vielen dank für die hilfe.
das problem wäre somit also gelöst!
gruß
Stahl1990