我有一个 OpenCL 内核用于一些计算。我发现只有一个线程使用 CPU 代码给出不同的结果。我使用的是vs2010 x64 Release模式。
通过一些示例检查 OpenCL 代码,我发现了一些有趣的结果。以下是内核代码中的测试示例。
我在OpenCl内核中测试了3种情况,精度通过printf("%.10f", fval);
检查
情况1:
float fval = (10296184.0) / (float)(x*y*z); // which gives result fval = 3351.6225585938
float fval = (10296184.0f) / (float)(x*y*z); // which gives result fval = 3351.6225585938
变量是:int x,y, z
这些值是通过一些操作计算出来的。它们的值为 x=12, y=16, z=16;
情况2:
float fval = (10296184.0) / (float)(12*16*16); // which gives result fval = 3351.6223144531
float fval = (10296184.0f) / (float)(12*16*16); // which gives result fval = 3351.6223144531
情况3:
但是,当我计算 fval
的差异时使用以上两个表达式,如果使用10296184.0
,结果为0 .
float fval = (10296184.0) / (float)(x*y*z) - (10296184.0) / (float)(12*16*16); // which gives result fval = 0.0000000000
float fval = (10296184.0f) / (float)(x*y*z) - (10296184.0f) / (float)(12*16*16); // which gives result fval = 0.0001812663
谁能解释一下原因或者给我一些提示吗?
最佳答案
一些观察:
两个 float
值相差 1 ULP 。因此结果存在最小程度的差异。
// Float ULP in the 2's place here
// v
0x1.a2f3ea0000000p+11 3351.622314... // OP's lower float value
0x1.a2f3eaaaaaaabp+11 3351.622395... // higher precision quotient
0x1.a2f3ec0000000p+11 3351.622558... // OP's higher float value
(10296184.0)/(float)(12*16*16)
在编译时计算,结果更接近预期的数学答案。
float fval = (10296184.0)/(float)(x*y*z)
在运行时计算。
考虑到使用的是 float
变量,令人惊讶的是代码使用 double
数学进行除法运算。这是一个 double
常量除以 double
(这是 float
产品的升级),结果是 double
> 商,转换为 float
然后保存。我希望使用 10296184.0f
- 请注意 f
- ,然后数学就可以全部作为 float
完成。
C 允许由 FLT_ROUNDS
表示的不同舍入模式,这在编译时和运行时可能有所不同,并且可能解释了差异。了解 fegetround() 的结果(该函数获取当前舍入方向。)会有所帮助。
OP可能采用了各种牺牲精度来换取速度的编译器优化。
<小时/>C 不指定数学运算的精度,但在高质量平台上,*/+ - sqrt() modf()
应该能达到最后的 ULP 效果。我怀疑代码的数学实现较弱。
关于c++ - OpenCL 内核浮点除法给出不同的结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39025822/