65,194
社区成员




// 开启omp并行,float计算误差累积到了0.2.
// 服了呀....
{
std::cout.precision(14);
std::array<float, 100000> da;
for (size_t i = 0; i < da.size(); i++)
da[i] = 1.0f * i / da.size();
long cnt = 0;
float cf = 0;
double cdb = 0;
#pragma omp parallel for
for (int i = 0; i < 100000; i++)
{
#pragma omp critical
{
cnt++;
cf += da[i];
cdb += da[i];
}
}
// x86 debug 输出: 100000 49999.6953125 49999.5 (49999.6953125这个值,还每次都不一样)
// x86 release 输出: 100000 49999.54296875 49999.5 (49999.54296875这个值,还每次同样不一样)
std::cout << cnt << " " << cf << " " << cdb << std::endl;
cnt = 0;
cf = 0;
cdb = 0;
for (int i = 0; i < 100000; i++)
{
cnt++;
cf += da[i];
cdb += da[i];
}
// x86 debug 输出: 100000 49999.51171875 49999.5
// x86 release 输出: 100000 49999.51171875 49999.5
std::cout << cnt << " " << cf << " " << cdb << std::endl;
}
浮点数计算误差是一个常见的问题。这是因为浮点数在计算机中以二进制形式表示,而大多数实数(例如十进制中的小数)无法精确地用二进制表示,因此存在舍入误差。
有几种方法可以减少浮点数计算误差:
使用更高精度的数据类型:如果需要更高的精度,可以使用更长的或更高精度的浮点数类型,如double或long double。
使用对称数:对称数是一种表示方式,使得计算机在进行浮点数计算时没有舍入误差。例如,可以将浮点数表示为(sign, exponent, mantissa)的形式,其中sign为正负号,exponent为指数,mantissa为尾数。这样,正数和负数的指数就可以用相同的表示方式,从而避免了舍入误差。
使用数值稳定的算法:数值稳定的算法是指在计算过程中保持舍入误差较小或可控的算法。这些算法通常会在计算过程中减少舍入误差的影响,以确保计算结果的准确性。
使用并行计算库:一些并行计算库提供了数值稳定的的高精度计算函数,可以在并行计算中减少浮点数计算误差。
调整计算精度:如果可能的话,可以调整计算精度,以减少舍入误差的影响。例如,可以设置计算精度为某个容忍度,以接受一定的计算误差。
总之,在并行计算中,浮点数计算误差是无法完全避免的,但可以通过上述方法减少其影响,以确保计算结果的准确性。
在这个代码中,浮点数计算误差累积到了0.2,可能是由于多个线程同时对cf和cdb进行累加操作时导致的。为了解决这个问题,可以尝试以下方法:
使用OpenMP的reduction指令:将累加操作放在reduction指令中,让OpenMP自动处理并行计算中的累加操作。这样可以避免多个线程同时对同一个变量进行累加操作,从而减小误差。修改代码如下:
cpp
Copy code
#pragma omp parallel for reduction(+:cf,cdb)
for (int i = 0; i < 100000; i++)
{
cnt++;
cf += da[i];
cdb += da[i];
}
使用更高精度的数据类型:可以尝试使用double类型来存储浮点数,以提高计算的精度。修改代码如下:
cpp
Copy code
std::array<double, 100000> da;
减少并行度:减少并行计算的线程数量,使得每个线程负责的计算量更大,从而减小误差。可以通过设置OMP_NUM_THREADS环境变量来控制线程数量。
需要注意的是,浮点数计算误差是由于浮点数的存储方式和运算规则所导致的,这是一个普遍存在的问题。在进行浮点数计算时,需要始终注意并处理可能的误差。