Search code examples
cparallel-processingmpiopenmpi

MPI_Reduce arrays representing decimal values (C)


I have a sequential program that computes numeric values and stores the result in an array where each element represents a number "place."

For example, a computed result [2,4,5,1,1,8,9,3,1] represents a decimal value of 2.45118931

The format is such that result[0] is the value to the left of the decimal and result[1-n] represents the tenths, hundredths, etc. places to the right of the decimal

The computed result for each processor goes into digits, and is reduced into mpiResult:

        long unsigned int *digits;
        long unsigned int *mpiResult;

Once each process has computed its portion, I want to find the combined value result.

I've not been able to implement MPI_Reduce in a way that is getting me the results I want.

The closest I've gotten so far:

MPI_Reduce(digits, mpiResult, d, MPI_UNSIGNED_LONG, MPI_SUM, MPI_COMM_WORLD);

As far as I understand how the reduction sum works with arrays, if I have two processes with the following values

[2][3][4][1][9] (2.3419)

and

[2][6][6][7][3] (2.6673)

The result of a sum should be 5.0092

But a reduction sum would give me the value:

[4][9][10][8][12] (4.910812)

because it sums each element across all processes.

How can I use MPI_Reduce to get the result format I'm looking for? Or am I completely off base with this approach?


Solution

  • The only issue your implementation has is that you don't propagate the carry between your digits: The digits individually are summed up correctly, you just need to add a small postprocessing step

    Pseudocode (be careful as the last line writes out of bounds, in fact you might need to continue this loop until carry == 0):

    for i = n ... 1
        carry = digit[i] / 10      # compute the carry to the next digit
        digit[i] %= 10             # only leave the least significant digit
        if carry != 0
            digit[i - 1] += carry  # add the carry to the next higher digit
    

    In your case, you get

    [4][9][10][8][12]           
    [4][9][10][9][2]
    [4][10][0][9][2]
    [5][0][0][9][2] -> 5.0092
    

    However, you need to allocate additional space in front of the most significant digit in case you decide to add something like 6.1582 and 7.4638

    Another possible approach would be to implement a custom MPI data type and user-defined operation to propagate the carry during reduction, however for this, you would probably need to make sure that there is enough space to contain the result before starting the reduction.