Search code examples
visual-c++floating-pointopenmpreduction

OpenMP for loop cause wrong result


I want to compare the loop performance with openmp by part of simple code. But the result is wrong.

I already use reduction to avoid race condition but never work.

here is my code: thanks for any suggestion

void TestMP_1(){
    float afValueTmp[MP_TEST_NUM] = { 0 }; // MP_TEST_NUM = 10000
    float sum = 0, sumNoMP = 0;
    float fDiff = 0;
    double eTDiff = 0;
    double t0 = 0;
    double t1 = 0;

    for (int i = 0; i < MP_TEST_NUM; i++)
    {
        afValueTmp[i] = i;
    }

    t0 = (double)getTickCount();
    for (int i = 0; i < MP_TEST_NUM; i++)
    {
        for (int k = 0; k < MP_TEST_NUM; k++);  // just for delay

        sumNoMP += afValueTmp[i];   // equation 4
    }

    t0 = ((double)getTickCount() - t0) / getTickFrequency();
    t1 = (double)getTickCount();

    #pragma omp parallel for reduction( +:sum)
    for (int i = 0; i < MP_TEST_NUM; i++)
    {
        for (int k = 0; k < MP_TEST_NUM; k++);  // just for delay

        sum += afValueTmp[i];
    }

    t1 = ((double)getTickCount() - t1) / getTickFrequency();
    eTDiff = t0 - t1;   // time improve
    fDiff = sum - sumNoMP;  // check result
    printf("%.3f\n", eTDiff);
}

Solution

  • You're facing floating point accuracy issues. Please allow me to elaborate:

    #include <stdio.h>
    
    int main(void)
    {
        float myOrigNumber = 49995000;
        float myNumber = myOrigNumber + 1.;
    
        printf ("orig: %f new: %f diff: %f\n",
                myOrigNumber, myNumber, myNumber-myOrigNumber);
        return 0;
    }
    

    The result will be:

    orig: 49995000.000000 new: 49995000.000000 diff: 0.000000
    

    So, where did that +1 go?

    The float type only has 7 to 8 significant digits. It doesn't matter where they are, because floats are internally always represented in Scientific notation as x.xxE+yy notation, where x.xx has 24 bits and yy has 8 bits. The number 49995001 is larger than 2^24 (16,777,216), so it will be rounded to the closest number that can be accurately represented, which apparently is 49995000.

    This is why using double for sum will alleviate your pain. It's not a real solution, though. Reduction operations have the requirement that the operation must be commutative. But that's not necessarily the case for floating point addition: if you add hundred times 1 and then 49995000 to sum, the result will be different from when you first add 1 and 49995000, and then ninety-nine times 1: in the second case, every later +1 will be rounded down as shown above.