我正在运行以下OpenMP代码
#pragma omp parallel shared(S2,nthreads,chunk) private(a,b,tid)
{
tid = omp_get_thread_num();
if (tid == 0)
{
nthreads = omp_get_num_threads();
printf("\nNumber of threads = %d\n", nthreads);
}
#pragma omp for schedule(dynamic,chunk) reduction(+:S2)
for(a=0;a<NREC;a++){
for(b=0;b<NLIG;b++){
S2=S2+cos(1+sin(atan(sin(sqrt(a*2+b*5)+cos(a)+sqrt(b)))));
}
} // end for a
} /* end of parallel section */
对于NREC = NLIG = 1024和更高的值,在8核心板中,我获得了7次加速。问题是如果我比较变量S2的最终结果,它与串行版本中获得的确切结果相差1到5%。可能是什么原因?我应该使用一些特定的编译选项来避免这种奇怪的浮动行为吗?
答案 0 :(得分:2)
浮点数的加/减顺序会影响精度。
举一个简单的例子,假设您的机器存储了2位小数,并且您计算的值是1 + 0.04 + 0.04。
如果先进行左侧加法,则得到1.04,将其舍入为1.第二次加法将再次给出1,因此最终结果为1.
如果你先做正确的加法,你得到0.08。添加到1,这将给出1.08,其舍入为1.1。
为获得最高精度,最好添加从小到大的值。
另一个原因可能是CPU上的浮点寄存器可能包含的位数多于主存储器中的浮点数。因此,如果某个中间结果缓存在寄存器中,则更准确,但如果它被换出到内存则会被截断。
答案 1 :(得分:0)
众所周知,当减去两个大值(或者添加两个具有不同符号的大值)时,机器浮点运算是有缺陷的,因此产生小的差异。因此,对振荡符号序列求和可能在每次迭代时引入严重错误。另一个有缺陷的案例是两个操作数的大小差别很大 - 较小的操作数实际上取消了它们
分离正负操作数并分别对每个组进行求和,然后加上(减去)组结果可能很有用。
如果准确性至关重要,则可能需要对每个组进行预先排序,并在每个组内执行两次求和。第一个总和将从中心向最大(头部),第二个将从最小(尾部)朝向中心。结果组总和将是部分运行的总和。