着色器中实现双精度cos()的结果是NaN,但在CPU上运行良好.出了什么问题

The result of own double precision cos() implemention in a shader is NaN, but works well on the CPU. What is going wrong?

本文关键字:运行 CPU 但在 问题 什么 NaN 实现 双精度 cos 结果是      更新时间:2023-10-16

正如我所说,我想在GLSL的计算着色器中实现我自己的双精度cos()函数,因为只有一个内置的float版本。

这是我的代码:

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;
}

这段代码的结果是,着色器上的总和是NaN,但在CPU上,算法运行良好。我也试着调试这个代码,我得到了以下信息:

  • 对于所有条目,facility[i]都是正的,而不是零
  • tempExp在每个步骤中都为正
  • 在每个步骤中,其他变量都不是NaN
  • 第一个时间和是NaN,处于i=4的阶跃

现在我的问题是:如果每个变量都是一个数字,没有任何东西被零除,特别是当算法在CPU上工作时,到底会出什么问题?

让我猜一猜:

首先,您确定问题在循环中,并且只使用以下操作:+*/

从这些操作生成NaN的规则是:

  • 分区0/0±∞/±∞
  • 乘法0×±∞±∞×0
  • 加法∞ + (−∞)(−∞) + ∞和等价减法

您通过声明faculty[]已正确初始化,排除了0/0±∞/±∞的可能性。

变量sign总是1.0-1.0,因此它不能通过*操作生成NaN

如果tempExp变成±∞,则剩下的是+操作。

所以可能tempExp在进入函数时太高,变成了±∞,这也会使sum变成±∞。在下一次迭代中,您将通过:∞ + (−∞)触发NaN生成操作。这是因为您将加法的一侧乘以sign,并且在每次迭代时符号在正和负之间切换。

您正试图将cos(x)近似为0.0。因此,您应该使用cos()函数的属性将输入值减少到接近0.0的值。理想地在CCD_ 33的范围内。例如,去除2*pi的倍数,并通过计算0.0周围的sin(x)等来获得[pi/4, pi/2]cos()的值。

可能出现严重错误的是精度损失。CCD_ 39通常通过范围缩减来实现,随后是用于范围CCD_。测距使用cos(x+2*pi) = cos(x)。但这种范围缩小并不完美。首先,pi不能用有限数学精确表示。

现在,如果你尝试像cos(1<<30)这样荒谬的东西,会发生什么?距离缩减算法很可能在x中引入了大于2*pi的误差,在这种情况下,结果是没有意义的。在这种情况下返回NaN是合理的。