user16612111
user16612111

Reputation:

My matrix vector multiplication is returning incorrect values

After digging deep into the internet I managed to read all the numbers in my csv to a matrix vector and also for the other single dimension vector from its related csv. The matrix csv file contains a matrix in the following format

91,86,94
12,54,88
79,58,66

The other input vector file contains the members of the one dimension vector as follows

14
20
22

So I expect the output Vector as a result of this multiplication to be for the first row as

91*14+86*20+94*22=5062

Instead of the above my C code is giving me an insane -1469150284 as the member of the first row of the resultant Vector, I suspected the initialization of the two dimension matrix at first but then even after using memset() to set all elements in the array to 0, I still get the same incorrect values.

The complete code on how I read the csvs and how I load each number into the arrays and how I multiply is provided below, help me trace the bug that is causing the multiplication error

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main() {
   //define the three files for reading
   FILE *matFile = fopen("test1_input_mat.csv", "r");
   FILE *vecFile = fopen("test1_input_vec.csv", "r");
   //we are writing to the below file
   FILE *outFile = fopen("test1_out_vec.csv", "w");

   //make sure the file exists
   if (matFile == NULL) {
       printf("%s","File does not exist");
       //break and return an exit code to the operating system
       return 99;
   }
   //define the dimensions of the matrix
   int x = 3;
   int y = 3;
   //allocate memory to the matrix dynamically
    int (*matrix_array)[x] = malloc(sizeof(int[x][y]));
    //initialize all the members to zero
    memset(matrix_array, 0, sizeof(matrix_array));
    //read from the matFile and assign to the vector
    char *r, l;
    //create a buffer variable for the read file process
    char buffer[255];
    char line[255] = "";
    char *replaced = NULL;

    while (fgets(buffer, sizeof(buffer), matFile)) {
        strncat(line, buffer, 255);
    }
    // printf("%s",line);
    replaced = replaceWord(line, "\n", ",");
    //printf("%s", replaced);
    //now that we have the elements of the file in a line
    //separated by commas
    char delim[] = ",";
    char *token;
    //get the first token
    token = strtok(replaced, delim);
    //walk through other tokens
    while (token != NULL) {
        //parse this and add it to the array
        int sub = atoi(token);
        //assign the number to th array
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                matrix_array[i][j] = sub;
            }
        }
        token = strtok(NULL, delim);
    }
    //allocate memory to one dimension array
    int *vec = (int *)malloc(3 * sizeof(int));
    //section below handles the parsing of numbers from the vector file
    char vline[255] = "";
    char vbuffer[255];
    char concatenated[255];
    char *replacing;

    //read the vector file
    while (fgets(vbuffer, 255, vecFile)) {
        strncat(concatenated, vbuffer, sizeof(vbuffer));
    }
    //replace the new line characters with commas
    replacing = replaceWord(concatenated, "\n", ",");
    //now parse that into the one dimension vector
    char *vtoken;
    //get the first token
    vtoken = strtok(replacing, delim);
    //get the rest of the tokens
    while (vtoken != NULL) {
        int no = atoi(vtoken);
        //append the numbers to the one dimension vector
        for (int i = 0; i < 3; i++) {
            vec[i] = no;
        }
        vtoken = strtok(NULL, delim);
    }
    //this is the section where we do the multiplication of the two
    int *out_vec = (int *)malloc(3 * sizeof(int));
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 3; j++) {
            //assign the product of the multiplication to the right index in the vector
            out_vec[j] += matrix_array[i][j] * vec[j];
        }
    }
    //write the integers in the second to the out file
    char str[255];
    char fin[255];
    for (int i = 0; i < 3; i++) {
        printf("%d\n", out_vec[i]);
    }

    //close the matFile
    fclose(matFile);
}

//method to replace the newline characters with commas

This code helped me replace the new line characters with commas in the single line comprised of all lines in the file

//this method replaces a string in the target string with another string

char *replaceWord(const char *s, const char *oldW,
                  const char *newW)
{
    char *result;
    int i, cnt = 0;
    int newWlen = strlen(newW);
    int oldWlen = strlen(oldW);

    // Counting the number of times old word
    // occur in the string
    for (i = 0; s[i] != '\0'; i++) {
        if (strstr(&s[i], oldW) == &s[i]) {
            cnt++;

            // Jumping to index after the old word.
            i += oldWlen - 1;
        }
    }

    // Making new string of enough length
    result = (char *)malloc(i + cnt * (newWlen - oldWlen) + 1);

    i = 0;
    while (*s) {
        // compare the substring with the result
        if (strstr(s, oldW) == s) {
            strcpy(&result[i], newW);
            i += newWlen;
            s += oldWlen;
        } else
            result[i++] = *s++;
    }

    result[i] = '\0';
    return result;
}

Upvotes: 1

Views: 99

Answers (1)

001
001

Reputation: 13533

I have tried to simplify and clean up your code as much as possible.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define ROWS 3
#define COLS 3

int main(int argc, char* argv[])
{
    FILE* matFile = fopen("test1_input_mat.csv", "r");
    if (!matFile) {
        puts("File does not exist");
        return 99;
    }
    int (*matrix_array)[ROWS] = malloc(sizeof(int[ROWS][COLS]));
    char buffer[255];
    for (int row = 0; row < ROWS; row++) {
        if (fgets(buffer, sizeof(buffer), matFile)) {
            char *p = strtok(buffer, ",");
            for (int col = 0; col < COLS; col++) {
                if (!p) return 99;
                matrix_array[row][col] = atoi(p);
                p = strtok(NULL, ",");
            }
        }
    }
    fclose(matFile);

    FILE* vecFile = fopen("test1_input_vec.csv", "r");
    if (!vecFile) {
        puts("File does not exist");
        return 99;
    }
    int* vec = malloc(ROWS * sizeof(int));
    for (int row = 0; row < ROWS; row++) {
        if (fscanf(vecFile, "%d", &vec[row]) != 1) return 99;
    }
    fclose(vecFile);

    int* out_vec = calloc(ROWS, sizeof(int));
    for (int row = 0; row < ROWS; row++){
        for (int col = 0; col < COLS; col++){
            out_vec[row] += matrix_array[row][col] * vec[col];
        }
    }

    for (int row = 0; row < ROWS; row++){
        printf("%d\n", out_vec[row]);
    }
    
    free(matrix_array);
    free(vec);
    free(out_vec);

    return 0;
}

One thing still to do is check the return values from malloc and calloc. Though it is unlikely in a small program like this, they can return NULL

Also, since you have hard-coded the size to be 3x3, there is no need for malloc/calloc.

#define ROWS 3
#define COLS 3
...
int matrix_array[ROWS][COLS];
int vec[ROWS];
int out_vec[ROWS] = {0}

Upvotes: 1

Related Questions