着色器中自己的双精度 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<<30)
这样荒谬的事情会发生什么?范围缩减算法很可能会在x
中引入大于2*pi
的错误,在这种情况下,结果毫无意义。在这种情况下返回NaN
是合理的。
【讨论】:
以上是关于着色器中自己的双精度 cos() 实现的结果为 ON,但在 CPU 上运行良好。出了啥问题?的主要内容,如果未能解决你的问题,请参考以下文章