omp 并行运算,float计算误差那么大,怎么办_(语言-c++)

该问题来自社区帖: https://bbs.csdn.net/topics/616853342.为符合问答规范, 该问题经过ChatGPT优化
// 开启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;
}

 

float误差打球,你用用精度更高的double

不知道你这个问题是否已经解决, 如果还没有解决的话:
  • 以下回答由chatgpt基于相关博客总结生成:

    这个问题的根本原因是由于并行计算过程中的竞态条件,导致计算结果产生了误差。要解决这个问题,可以采取以下几个步骤:

    1. 将浮点计算量尽量减少:在示例代码中,浮点计算的位置都被#pragma omp critical所保护,这导致了所有的浮点计算都是串行执行的,无法充分利用并行性能。所以,首先要将浮点计算的粒度调整到最小,避免将不必要的计算放在临界区内。可以将#pragma omp critical注释掉,使得所有线程都可以并行执行计算。

    2. 采用精度更高的数据类型:示例代码中使用的是float类型进行计算,这个数据类型的精度有限,容易产生误差。可以尝试将float改为double,或者使用更高精度的库来进行浮点计算,如boost的multiprecision库。

    3. 采用更合理的算法:有时候,误差的产生也与算法的选择有关。可以探索更加稳定的数值计算算法,避免误差的累积。可以参考数值计算的经典算法,如Kahan summation算法等。

    修改后的示例代码如下所示:

    {
        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;
        double cf = 0;
        double cdb = 0;
    #pragma omp parallel for         
        for (int i = 0; i < 100000; i++)
        {
            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;
    }
    

    希望以上解决方案能够解决您所遇到的问题,如果还有其他疑问,请随时提出。


如果你已经解决了该问题, 非常希望你能够分享一下解决方案, 写成博客, 将相关链接放在评论区, 以帮助更多的人 ^-^