Das Ergebnis der eigenen cos () - Doppelpräzision-Implementierung in einem Shader ist NaN, funktioniert aber gut auf der CPU. Was läuft falsch?

8

Wie gesagt, ich möchte meine eigene Double-Precision-Funktion cos () in einem Compute-Shader mit GLSL implementieren, da es nur eine eingebaute Version für float gibt.

Das ist mein Code:

%Vor%

Das Ergebnis dieses Codes ist, dass die Summe im Shader NaN ist, aber auf der CPU funktioniert der Algorithmus gut. Ich habe versucht, auch diesen Code zu debuggen und ich habe die folgenden Informationen erhalten:

  • faculty [i] ist positiv und nicht Null für alle Einträge
  • tempExp ist in jedem Schritt positiv
  • Keine der anderen Variablen ist NaN während jedes Schritts
  • die erste Zeitsumme ist NaN ist auf dem Schritt mit i = 4

und jetzt meine Frage: Was genau kann schief gehen, wenn jede Variable eine Zahl ist und nichts durch Null geteilt wird, besonders wenn der Algorithmus auf der CPU arbeitet?

    
DanceIgel 05.03.2015, 11:50
quelle

2 Antworten

1

Lass mich raten:

Zuerst haben Sie festgestellt, dass das Problem in der Schleife liegt, und Sie verwenden nur die folgenden Operationen: + , * , / .

Die Regeln zum Generieren von NaN von diesen Operationen sind:

  • Die Bereiche 0/0 und ±∞/±∞
  • Die Multiplikationen 0×±∞ und ±∞×0
  • Die Zusätze ∞ + (−∞) , (−∞) + ∞ und äquivalente Subtraktionen

Sie haben die Möglichkeit für 0/0 und ±∞/±∞ ausgeschlossen, indem Sie angeben, dass faculty[] korrekt initialisiert wurde.

Die Variable sign ist immer 1.0 oder -1.0 und kann daher NaN nicht durch die Operation * erzeugen.

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

Wahrscheinlich ist tempExp bei der Eingabe Ihrer Funktion zu hoch und wird ±∞ , dies macht sum auch ±∞ . Bei der nächsten Iteration triggern Sie den NaN -Erzeugungsvorgang durch: ∞ + (−∞) . Dies liegt daran, dass Sie eine Seite der Addition mit sign multiplizieren und das Zeichen bei jeder Iteration zwischen positiv und negativ wechselt.

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

    
fjardon 13.11.2015 13:14
quelle
0

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

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

    
MSalters 13.11.2015 13:30
quelle

Tags und Links