Странное поведение с плавающей точкой в ​​OpenMP - PullRequest
1 голос
/ 20 января 2011

Я использую следующий код 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% от точных результатов, полученных в серийной версии. Что может быть причиной? Должен ли я использовать некоторые конкретные параметры компиляции, чтобы избежать этого странного поведения с плавающей точкой?

Ответы [ 2 ]

2 голосов
/ 20 января 2011

Порядок сложений / вычитаний чисел с плавающей запятой может влиять на точность.

В качестве простого примера предположим, что ваша машина хранит 2 десятичных знака и что вы вычисляете значение 1 + 0,04 + 0,04.

  • Если вы сначала выполните левое сложение, вы получите 1,04, которое округляется до 1. Второе сложение снова даст 1, поэтому окончательный результат равен 1 *.

  • Если вы сделаете правильное дополнение первым, вы получите 0,08. Если добавить к 1, получится 1,08, округленное до 1,1.

Для максимальной точности лучше добавлять значения от малого к большому.

Другая причина может заключаться в том, что плавающие регистры в ЦП могут содержать больше битов, чем плавающие в основной памяти. Следовательно, если какой-то промежуточный результат кэшируется в регистре, он более точен, но если он выгружается в память, он усекается.

См. Также этот вопрос в C ++ FAQ .

0 голосов
/ 21 января 2011

Известно, что машинные операции с плавающей точкой ошибочны, когда вычитаются два больших значения (или добавляются два больших значения с разными знаками), что приводит к небольшой разнице в результате. Таким образом, суммирование последовательностей осциллирующих знаков может вносить серьезную ошибку на каждой итерации. Еще один ошибочный случай, когда величины двух операндов сильно различаются - меньший операнд фактически отменяет себя.
Может быть полезно отделить положительные и отрицательные операнды и выполнить суммирование каждой группы отдельно, а затем сложить (вычесть) результаты группы.
Если точность имеет решающее значение, это, вероятно, потребует предварительной сортировки каждой из групп и выполнения двух сумм внутри каждой. Первая сумма будет идти от центра к наибольшему (голова), вторая - от самого маленького (хвост) к центру. Результирующая групповая сумма будет суммой частичных прогонов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...