#define LIM 1000000000
double calculate(long value)
{
double toReturn = 0.;
for( ; value ; value-- )
toReturn += 1.0 / (double)value;
return toReturn;
}
int main()
{
printf("%.5f\n",calculate(LIM));
}
The execution of this program yields a result of
21.30048
That differs a lot from the result you indicate, 15.40368
. The reason? float
has only 6 representative digits, the rest can be considered junk. One can think that 6 digits give a lot of precision ... but in your case, that is, in 1e9 iterations the error is significant. In other words, when i>1e7
you are already accumulating an error of approximately 10 and in% co_of% of 100.
While working with i>1e7
for this operation, you will never get a correct result.
On the other hand, your code has a race condition:
#pragma omp for
for(i = 1;i <= LIM;i++)
serie += (1 / (float)i); // <<--- AQUI!!!
If we exploit the code a bit we get something similar to this:
#pragma omp for
for(i = 1;i <= LIM;i++)
{
double temp = serie; // (1)
temp = temp + 1.0 / i;
serie = temp; // (2)
}
You are assuming that float
will deduct that openmp
is a variable common to all iterations and that you should take care that dirty readings do not occur that alter the final result and this is not the case. What happens is that one of the two threads can end up overwriting the value of the previous thread: the two threads pass through (1) at the same time and then through (2), what happens is that only the second value prevails.
To avoid this inconsistency you have to modify the definition of the loop:
#pragma omp parallel num_threads(2)
{
#pragma omp for reduction(+:serie)
for(i = 1;i <= LIM;i++)
serie += (1.0 / i);
}
What the serie
modifier does is that it assigns to each execution thread a copy of reduction
, in such a way that each thread will modify its own version of the variable. When both threads are finished, the two versions of the variable are added together and the result is stored in the variable serie
that you have defined.
#pragma omp for reduction(+:serie)
// ^ ^^^
// | variable a dividir
// operacion a ejecutar para fusionar los valores
As a final note, serie
is not necessary in this case since the definition of the parallel loop includes the corresponding synchronization of the threads.
In short, your code should look like this:
#define LIM 1000000000
int main()
{
double serie = 0; // No olvides inicializar las variables
long i;
#pragma omp parallel num_threads(2)
{
#pragma omp for reduction(+:serie)
for(i = 1;i <= LIM;i++)
serie += (1.0 / i);
}
printf("La Serie es: %.5f\n",serie);
return 0;
}
NOTE : If you still have any doubts about the final result you can check the correct value in this link