Tap*_*ped 5 c++ windows precision opencl
我在OpenCL中发现了主机 - 客户端浮点标准的问题.问题是,在x86中编译时,Opencl计算的浮点数与我的visual studio 2010编译器的浮点数不同.但是,在x64中进行编译时,它们处于相同的限制.我知道它必须与之相关,http://www.viva64.com/en/b/0074/
我在测试过程中使用的来源是:http://www.codeproject.com/Articles/110685/Part-1-OpenCL-Portable-Parallelism
当我在x86中运行程序时,它会给我202个相同的数字,当时内核和C++程序占用了1269760个数字.然而,在64位构建中,1269760数字是正确的,换句话说是100%.此外,我发现opencl和x86 c ++的计算结果之间的误差是5.5385384e-014,这是一个非常小的部分但不够小,与数字的epsilon相比,即2.92212543378266922312416e-19.
这是因为,错误需要小于epsilon,因此程序可以将这两个数字识别为一个相同的数字.当然,通常人们永远不会比较本地的浮子,但很高兴知道浮动限制是不同的.是的,我试图设置flt:static,但得到了同样的错误.
所以我想对这种行为做一些解释.提前感谢所有答案.
由于在将项目从x86切换到x64时GPU代码没有任何变化,所以这一切都必须像在CPU上执行乘法一样.在x86和x64模式下浮点数处理之间存在一些细微差别,最大的一点是,由于任何x64 CPU也支持SSE和SSE2,因此默认情况下它用于Windows上64位模式下的数学运算.
HD4770 GPU使用单精度浮点单元完成所有计算.另一方面,现代x64 CPU有两种处理浮点数的功能单元:
在32位模式下,编译器不会假设SSE可用并生成通常的x87 FPU代码来进行数学运算.在这种情况下,类似data[i] * data[i]的操作是使用更高的80位精度在内部执行的.该类型的比较if (results[i] == data[i] * data[i])如下进行:
data[i] 使用.将其推入x87 FPU堆栈 FLD DWORD PTR data[i]data[i] * data[i] 使用计算 FMUL DWORD PTR data[i]result[i] 使用将其推入x87 FPU堆栈 FLD DWORD PTR result[i]FUCOMPP这就是问题所在.data[i] * data[i]以80位精度驻留在x87 FPU堆栈元素中.result[i]来自GPU的32位精度.这两个数字很可能会有所不同,因为data[i] * data[i]有更多的有效数字,而result[i]有很多零(80位精度)!
在64位模式下,事情以另一种方式发生.编译器知道您的CPU支持SSE,它使用SSE指令进行数学运算.在x64上以下列方式执行相同的比较语句:
data[i] 使用加载到SSE寄存器中 MOVSS XMM0, DWORD PTR data[i]data[i] * data[i] 使用计算 MULSS XMM0, DWORD PTR data[i]result[i] 使用加载到另一个SSE寄存器 MOVSS XMM1, DWORD PTR result[i]UCOMISS XMM1, XMM0在这种情况下,使用与GPU上使用的相同的32位单点精度执行平方运算.不会生成80位精度的中间结果.这就是为什么结果是一样的.
即使没有涉及GPU,也很容易实际测试.只需运行以下简单程序:
#include <stdlib.h>
#include <stdio.h>
float mysqr(float f)
{
f *= f;
return f;
}
int main (void)
{
int i, n;
float f, f2;
srand(1);
for (i = n = 0; n < 1000000; n++)
{
f = rand()/(float)RAND_MAX;
if (mysqr(f) != f*f) i++;
}
printf("%d of %d squares differ\n", i);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
mysqr是专门编写的,以便中间80位结果将以32位精度进行转换float.如果您在64位模式下编译并运行,则输出为:
0 of 1000000 squares differ
Run Code Online (Sandbox Code Playgroud)
如果编译并以32位模式运行,则输出为:
999845 of 1000000 squares differ
Run Code Online (Sandbox Code Playgroud)
原则上你应该能够在32位模式下更改浮点模型(项目属性 - >配置属性 - > C/C++ - >代码生成 - >浮点模型),但这样做至少在VS2010中间没有任何改变结果仍保留在FPU中.您可以做的是强制存储和重新加载计算的方块,以便在将其与GPU的结果进行比较之前将其舍入为32位精度.在上面的简单示例中,这是通过更改:
if (mysqr(f) != f*f) i++;
Run Code Online (Sandbox Code Playgroud)
至
if (mysqr(f) != (float)(f*f)) i++;
Run Code Online (Sandbox Code Playgroud)
更改后,32位代码输出变为:
0 of 1000000 squares differ
Run Code Online (Sandbox Code Playgroud)