我用c ++ amp计算了这个值。环境:VS2015,Win8。
运行parallel_for_each函数时,值为NaN。原因是concurrency::fast_math::tanh
函数。
concurrency::fast_math::tanh
运行时, 1000
函数在参数大于parallel_for_each
时返回NaN:
float arr[2];
concurrency::array_view<float> arr_view(2, arr);
concurrency::extent<1> ex;
ex[0] = 1;
parallel_for_each(ex, [=](Concurrency::index<1> idx) restrict(amp){
float t = 10000000;
arr_view[0] = concurrency::fast_math::fabs(t);
arr_view[1] = concurrency::fast_math::tanh(t);
});
arr_view.synchronize();
std::cout << arr[0] << "," << arr[1] << std::endl;
输出
1e+07,nan
case2,如果没有运行parallel_for_each:
float arr[2];
concurrency::array_view<float> arr_view(2, arr);
concurrency::extent<1> ex;
ex[0] = 1;
float t = 10000000;
arr_view[0] = concurrency::fast_math::fabs(t);
arr_view[1] = concurrency::fast_math::tanh(t);
arr_view.synchronize();
std::cout << arr[0] << "," << arr[1] << std::endl;
输出:
1e+07,1
这是我所期待的结果。 如果将tanh更改为tanhf结果是相同的。
为什么tanh函数会返回NaN? 为什么,只在运行parrallel_for_each时返回NaN? 请告诉我原因和问题的解决方案。
答案 0 :(得分:2)
fast_math
中定义的函数优先考虑速度超过精度。实现和精度取决于硬件。当您不使用parallel_for_each
语法时,代码将在CPU上运行,该代码仅实现一个&#34;精确的&#34; tanh
功能,因此给出了正确的答案。
要解决此问题,您可以调用precise_math
,
concurrency::precise_math::tanh(t);
如果这个太慢并且fast_math::tanh
的精度足够,你可以尝试类似的东西
double myTanh(double t){
return (concurrency::fast_math::fabs(t)>100) ? concurrency::precise_math::copysign(1,t) : concurrency::fast_math::tanh(t);
}
它可能会或可能不会比精确版本运行得更快,具体取决于硬件。所以你需要进行一些测试。
答案 1 :(得分:0)
concurrency::fast_math
中的大多数功能都不能保证返回正确的值。其中一些(如tanh)甚至可以返回NaN值。在我的HD 6870上,所有数字超过90的快速tanh返回NaN。
以下是解决此问题的一些技巧。
你可以将Tanh参数“绑定”到10
float Tanh(float val) restrict(amp)
{
if (val > 10)
return 1;
else if (val < -10)
return-1;
return Concurrency::fast_math::tanh(val);
}
这不会导致任何精度损失,因为float只有7位精度,而difference between Tanh(10) and 1是4 * 10 -9
或者,你可以实现你自己的Tanh函数,它没有这样的限制。
float Tanh(float val) restrict(amp)
{
float ax = fabs(val);
float x2 = val * val;
float z = val * (1.0f + ax + (1.05622909486427f + 0.215166815390934f * x2 * ax) * x2);
return (z / (1.02718982441289f + fabs(z)));
}
很久以前在某处找到了这个tanh近似值。它非常快速且相当精确。
但是,如果您需要非常准确,可以将concurrency::fast_math
替换为concurrency::precise_math
。但是这个选项有一个很大的缺点:precise_math
无法在许多GPU上运行(例如我的6870)。
来自here。
这些功能,包括 单精度函数,需要扩展的双精度支持 在加速器上。你可以使用 accelerator :: supports_double_precision数据成员,以确定您是否 可以在特定的加速器上运行这些功能。
此外,precise_math
可能比fast_math
慢10倍以上,特别是在非专业视频卡上。
如果你不在parallel_for_each
块中运行并发代码,看起来你实际上并没有使用gpu。所以,你评估在CPU上评估没有GPU特定的错误。实际上,如果你运行这段代码
float t = 0.65;
arr_view[1] = concurrency::fast_math::tanh(t);
parallel_for_each(e, [=](index<1> idx) restrict(amp)
{
arr_view[0] = concurrency::fast_math::tanh(t);
});
std::cout << arr[0] << "," << arr[1] << std::endl;
arr_view.synchronize();
std::cout << arr[0] << "," << arr[1] << std::endl;
std::cout << arr[0] - arr[1] << std::endl;//may return non-zero value, depending on gpu
你可以在同步之前看到第一个tanh的结果,同时获得parallel_for_each块的结果需要它。另外,对我而言,它返回的结果略有不同,但这可能取决于硬件。