我发现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,但得到了同样的错误。
所以我想对这种行为做一些解释。 提前感谢所有答案。
答案 0 :(得分:9)
由于在将项目从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]
将FLD DWORD PTR data[i]
推入x87 FPU堆栈
data[i] * data[i]
使用FMUL DWORD PTR data[i]
result[i]
使用FLD DWORD PTR result[i]
FUCOMPP
这就是问题所在。 data[i] * data[i]
以80位精度驻留在x87 FPU堆栈元素中。 result[i]
以32位精度来自GPU。这两个数字很可能会有所不同,因为data[i] * data[i]
有更多有效数字,而result[i]
有很多零(精度为80位)!
在64位模式下,事情以另一种方式发生。编译器知道你的CPU是否支持SSE,它使用SSE指令进行数学运算。在x64上以下列方式执行相同的比较语句:
data[i]
使用MOVSS XMM0, DWORD PTR data[i]
data[i] * data[i]
使用MULSS XMM0, DWORD PTR data[i]
result[i]
使用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;
}
专门编写 mysqr
,以便中间80位结果将以32位精度float
进行转换。如果在64位模式下编译并运行,则输出为:
0 of 1000000 squares differ
如果您在32位模式下编译并运行,则输出为:
999845 of 1000000 squares differ
原则上,您应该能够以32位模式更改浮点模型(项目属性 - &gt;配置属性 - &gt; C / C ++ - &gt;代码生成 - &gt;浮点模型)但这样做没有任何改变,因为至少在VS2010上,中间结果仍保留在FPU中。您可以做的是强制存储和重新加载计算的方形,以便在与GPU的结果进行比较之前将其舍入到32位精度。在上面的简单示例中,这可以通过更改:
来实现if (mysqr(f) != f*f) i++;
到
if (mysqr(f) != (float)(f*f)) i++;
更改后,32位代码输出变为:
0 of 1000000 squares differ
答案 1 :(得分:-1)
就我而言
(float)(f*f)
没有帮助。我用了
correct = 0;
for(unsigned int i = 0; i < count; i++) {
volatile float sqr = data[i] * data[i];
if(results[i] == sqr)
correct++;
}
代替。