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:
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?
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:
0/0
und ±∞/±∞
0×±∞
und ±∞×0
∞ + (−∞)
, (−∞) + ∞
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.
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.