2015-03-05 7 views
8

wie ich schon sagte, ich möchte meine eigene Double-Precision-Funktion cos() in einem Compute-Shader mit GLSL implementieren, weil es nur eine eingebaute Version für float gibt.Das Ergebnis der eigenen Double-Cos() - Implementierung in einem Shader ist NaN, funktioniert aber gut auf der CPU. Was läuft falsch?

Dies ist mein Code:

double faculty[41];//values are calculated at the beginning of main() 

double myCOS(double x) 
{ 
    double sum,tempExp,sign; 
    sum = 1.0; 
    tempExp = 1.0; 
    sign = -1.0; 

    for(int i = 1; i <= 30; i++) 
    { 
     tempExp *= x; 
     if(i % 2 == 0){ 
      sum = sum + (sign * (tempExp/faculty[i])); 
      sign *= -1.0; 
     } 
    } 
return sum; 
} 

Das Ergebnis dieses Codes ist, dass die Summe NaN am Shader sich herausstellt, aber auf der CPU und der Algorithmus funktioniert. Ich habe versucht, zu diesem Code zu debuggen, und ich habe die folgenden Informationen:

  • Fakultät [i] positiv ist und nicht null für alle Einträge
  • tempExp in jedem Schritt positiv ist
  • keiner der anderen Variablen bei jedem Schritt sind NaN
  • das erste Mal Summe ist NaN im Schritt mit i = 4

und nun meine Frage: Was genau schief gehen kann, wenn jede Variable eine Zahl und nichts wird geteilt insbesondere dann, wenn der Algorithmus auf der CPU arbeitet?

+5

Unendlichkeit geteilt durch Unendlichkeit, zum Beispiel. Was sind deine Eingabedaten? – Wintermute

+0

http://StackOverflow.com/a/4430934/17034 –

+0

@Wintermute Ich bin nicht wirklich sicher darüber und es ist schwer, genaue Werte in einem Shader zu schätzen. Ein Teil davon ist positiv und der Rest ist negativ. Aber ich werde versuchen, sie zu schätzen. – DanceIgel

Antwort

1

Lassen Sie mich raten:

Zuerst bestimmt das Problem in der Schleife ist, und verwenden Sie nur die folgenden Operationen: +, *, /.

Die Regeln für NaN aus diesen Operationen zu erzeugen sind:

  • Die Divisionen 0/0 und ±∞/±∞
  • Die Multiplikationen 0×±∞ und ±∞×0
  • Die Zugänge ∞ + (−∞), (−∞) + ∞ und äquivalente Subtraktionen

Du rulst die Möglichkeit für 0/0 und ±∞/±∞ durch die Angabe, dass faculty[] ist richtig initialisiert.

Die Variablen sign ist immer 1.0 oder -1.0 so kann es nicht die NaN durch den * Betrieb erzeugen.

Was bleibt, ist die + Operation, wenn tempExp jemals ±∞ werden.

So wahrscheinlich tempExp auf Eingabe Ihrer Funktion zu hoch ist und wird ±∞, wird diese sum machen ±∞ zu sein. Bei der nächsten Iteration werden Sie den NaN Erzeugungsvorgang auslösen durch: ∞ + (−∞). Dies liegt daran, dass Sie eine Seite der Addition mit sign multiplizieren und bei jeder Iteration zwischen positiv und negativ wechseln.

Sie versuchen, cos(x) um 0.0 anzunähern.Daher sollten Sie die Eigenschaften der Funktion cos() verwenden, um den Eingabewert auf einen Wert nahe 0.0 zu reduzieren. Idealerweise im Bereich [0, pi/4]. Zum Beispiel, entfernen Sie Vielfache von 2*pi, und erhalten Sie die Werte cos() in [pi/4, pi/2] durch Berechnung sin(x) um 0.0 und so weiter.

0

Was dramatisch schief gehen kann, ist ein Genauigkeitsverlust. cos(x) wird normalerweise durch Bereichsreduzierung implementiert, gefolgt von einer dedizierten Implementierung für den Bereich [0, pi/2]. Bereichsreduzierung verwendet cos(x+2*pi) = cos(x). Aber diese Reichweitenreduktion ist nicht perfekt. Für den Anfang kann pi nicht genau in endlicher Mathematik dargestellt werden.

Was passiert nun, wenn Sie etwas so absurd wie cos(1<<30) versuchen? Es ist durchaus möglich, dass der Entfernungsreduktionsalgorithmus einen Fehler in x einführt, der größer ist als 2*pi, in welchem ​​Fall das Ergebnis bedeutungslos ist. Die Rückgabe von NaN in solchen Fällen ist zumutbar.