2010-02-10 5 views
8

Verwenden von Java auf einem Windows 7-PC (nicht sicher, ob das wichtig ist) und Aufrufen von Math.cos() auf Werten, die stattdessen 0 (wie Pi/2) zurückgeben sollten gibt kleine Werte zurück, aber kleine Werte, die, wenn ich nicht falsch verstanden habe, viel mehr als 1 ul von Null entfernt sind.Java Math.cos() Methode gibt 0 nicht zurück, wenn erwartet

Math.cos(Math.PI/2) = 6.123233995736766E-17 
Math.ulp(Math.cos(Math.PI/2)) = 1.232595164407831E-32 

Ist dies in der Tat innerhalb von 1 ULP und ich bin einfach verwirrt? Und wäre dies eine akzeptable Wrapper-Methode, um diese kleine Ungenauigkeit zu beheben?

public static double cos(double a){ 
    double temp = Math.abs(a % Math.PI); 
    if(temp == Math.PI/2) 
     return 0; 
    return Math.cos(a); 
} 
+0

@ dimo414 : Zuerst ein Kommentar zu Ihrer "nicht sicher, ob das zählt". Das Verhalten der meisten mathematischen Operationen ist nicht genau definiert, daher können Betriebssystem und CPU eine Rolle spielen. Wenn Sie eine mathematische Operation haben wollen, deren Verhalten streng definiert ist (die leichter zu beheben sind), verwenden Sie StrictMath, nicht Math (natürlich sollten StrictMath-Operationen wahrscheinlich langsamer sein, da sie nicht die hardwarebeschleunigte Operation in der CPU verwenden können). – SyntaxT3rr0r

+0

Math.cos() ist nur ein Wrapper für StrictMath.cos(), die selbst eine native Funktion ist. – dimo414

Antwort

10

Vergessen Sie nicht, dass Math.PI/2 eine Näherung ist. Es wird nicht genau pi/2, so das Ergebnis der cos(Math.PI/2) sein ist nicht sein werde genau 0. Math.cos kann eine ziemlich genaue Version des Kosinus des genauen Wertes durch die Berechnung Math.PI/2 zurückgegeben werden zurückkehren.

+0

Sehr wahr, aber das sagte, es scheint mir intuitiv, dass, wenn Math.PI das Doppelte ist, das Pi am nächsten ist, Math.PI/2 dem Pi/2 am nächsten sein sollte. Und da durch die Definition von cos() Pi/2 und 3Pi/2 Null sind, scheint es, wenn die nächste doppelte Annäherung dieser Zahlen an Math.cos() übergeben wird, sollte es auch Null sein. Vielleicht ist das einfach schlecht, (was der Kern meiner Frage ist), aber das macht für mich einen intuitiven Sinn. – dimo414

7

Sie sollten niemals == mit Doppel verwenden. Sie müssen immer innerhalb der Fehlermarge tun. 10 -17 ist eine gute Präzision, wenn Sie mich fragen. Ulp Figur von 10 -32 ist nur genau doppelt so groß wie in 10 -17 Größenordnung, wie 2,220446049250313E-16 ist die Präzision der Zahl in 10 Größenordnung.

+0

@Slartibartfsat: +1, genau. Du warst schneller als ich. – SyntaxT3rr0r

+0

Die Fehlermarge sollte mindestens zweimal ul (PI/2) sein, weil das die Ungenauigkeit von PI/2 ist. Da cos an diesem Punkt eine Ableitung von -1 hat, spiegelt sich die Ungenauigkeit von PI/2 im Ergebnis und die Ungenauigkeit von cos wider. – starblue

+0

Normalerweise würde ich Ihnen zustimmen, aber ich möchte meine Ausnahme auf so wenige Fälle wie möglich beschränken. Wenn also die Eingabe gerade/sehr/leicht von Math.PI/2 abweicht, lasse ich sie nativ berechnen, aber in In dem speziellen Fall, in dem es genau Math.PI/2 oder eines seiner Vielfachen ist, möchte ich, dass es genau null ist. Einige begrenzte Tests haben gezeigt, dass es funktioniert, zumindest auf meinem Computer. Ist diese Argumentation in Ordnung, oder sollte ich noch einen Bereich prüfen? – dimo414

2

Dies ist ein häufiger Fehler, wenn Sie beginnen, hat dieser Link eine sehr technische Diskussion der Gründe warum. http://docs.sun.com/source/806-3568/ncg_goldberg.html

Aber in der einfachsten Form, in der gleichen Art und Weise, die wir nicht genau 1/3 im Dezimalsystem darstellen können, gibt es Werte, die nicht genau in dem binären System dargestellt werden können

Verwandte Themen