none
Math.Sin liefert falschen Wert!? RRS feed

  • 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
    Dienstag, 26. Januar 2010 16:37

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
    Mittwoch, 27. Januar 2010 17:49

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!
    Dienstag, 26. Januar 2010 20:59
  • 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
    Mittwoch, 27. Januar 2010 10:49
  • @ 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

    Mittwoch, 27. Januar 2010 13:19
  • 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
    Mittwoch, 27. Januar 2010 17:49

  • 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
    Mittwoch, 27. Januar 2010 19:19