着色器中自己的双精度 cos() 实现的结果为 ON,但在 CPU 上运行良好。出了啥问题?

Posted

技术标签:

【中文标题】着色器中自己的双精度 cos() 实现的结果为 ON,但在 CPU 上运行良好。出了啥问题?【英文标题】:The result of own double precision cos() implemention in a shader is NaN, but works well on the CPU. What is going wrong?着色器中自己的双精度 cos() 实现的结果为 ON,但在 CPU 上运行良好。出了什么问题? 【发布时间】:2015-03-05 11:50:55 【问题描述】:

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

这是我的代码:

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 上算法运行良好。 我也尝试调试这段代码,得到以下信息:

faculty[i] 对于所有条目都是正数而不是零 tempExp 在每一步都是正数 在每个步骤中,其他变量都不是 NaN 第一次总和为 NaN 在 i=4 的步骤中

现在我的问题是:如果每个变量都是一个数字并且没有任何内容被零除,特别是当算法在 CPU 上运行时,究竟会出现什么问题?

【问题讨论】:

无穷除以无穷,例如。你的输入数据是什么? ***.com/a/4430934/17034 @Dancelgel:GLSL 有点奇怪,因为它允许内置浮点函数的精度和范围发生变化。您应该查看GLSL 4.50 specification 的第 83-84 页了解更多详细信息。 GLSL 甚至不需要对 NaN 的支持,但几乎所有 DX11 硬件(以及任何支持双精度的硬件)都有它。 提供教师的样本值或初始化它的代码将有很大帮助。 导致 NaN 的 x 值是多少? cos 算法的第一步是让 x 在 -pi 【参考方案1】:

让我猜猜:

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

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

0/0±∞/±∞ 部门 0×±∞±∞×0 的乘法 加法∞ + (−∞)(−∞) + ∞ 和等效减法

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

变量sign始终为1.0-1.0,因此无法通过*操作生成NaN

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

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

您正试图在 0.0 附近逼近 cos(x)。因此,您应该使用cos() 函数的属性将输入值减少到接近0.0 的值。理想情况下在[0, pi/4] 范围内。比如去掉2*pi的倍数,通过围绕0.0计算sin(x)得到[pi/4, pi/2]cos()的值等等。

【讨论】:

【参考方案2】:

可能会出现严重错误的是精度损失。 cos(x) 通常是通过范围缩小来实现的,然后是范围 [0, pi/2] 的专用实现。范围缩小使用cos(x+2*pi) = cos(x)。但这种范围缩小并不完美。对于初学者,pi 不能用有限数学精确表示。

现在,如果您尝试像 cos(1&lt;&lt;30) 这样荒谬的事情会发生什么?范围缩减算法很可能会在x 中引入大于2*pi 的错误,在这种情况下,结果毫无意义。在这种情况下返回NaN 是合理的。

【讨论】:

以上是关于着色器中自己的双精度 cos() 实现的结果为 ON,但在 CPU 上运行良好。出了啥问题?的主要内容,如果未能解决你的问题,请参考以下文章

OpenGL ES 2.0 符点精度

是否有可能消除three.js着色器中的引号和逗号的麻烦?

有没有办法在片段着色器中使用先前渲染的结果?

返回没有尾随零的双精度类型

格式,Java 中的双精度和整数的 2 位小数和 0

如何在android中添加两个没有指数的双精度值