0

For the past few hours I have been trying to build a C++ module that, upon requesting the input of the user on the size and contents of 2 matrices (restricted to ones that can be multiplied together), then proceeds to multiply them together and return the values of a third answer matrix. While the matrix input commands seem to work based on thorough testing, I can't seem to get a correct answer from my multiplication command, despite going through each step of the algorithm and how it corresponds to actual matrix multiplication. The first value of the answer matrix is correct, and then all succeeding values are incorrect by some factor. Does anyone have any insight as to what could be going wrong?

#include <iostream>
#include <cmath>
using namespace std;

int r, c, a1, a2, b1, b2;  //defines row and column indices
double m[1][1], m2[1][1], a[1][1];   //initializes matrices
double b;
int inflag = true;  
int repflag = false;

void defmatrix() {  //Defines the matrix size of the first inputted matrix
    cout << "Matrix Rows: ";
    cin >> r;
    cout << "Matrix Columns: ";
    cin >> c;
    
}

void fillmatrix() {  //Fills the matrix with automatic or user-inputted values
    if (inflag == true) {
        for (int i = 0; i < r; i++) {
            for (int j = 0; j < c; j++) {
               cout << "Number in row " << i + 1 << " and column " << j + 1 << ": ";
               cin >> b;
               m[i][j] = b;
            }
        }
    } else {
        for (int i = 0; i < r; i++) {
            for (int j = 0; j < c; j++) {
               m[i][j] = 1;
            }
        }
    }
    for (int i = 0; i < r; i++) {
        for (int j = 0; j < c; j++) {
           cout << m[i][j] << "   ";
        }
    }
}

void matrixmult() {  //Multiplication function for matrix math
    if (repflag == false) { 
        cout << "\n" << "Your second matrix will have " << c << " rows" << "\n";
        b1 = c;
        a1 = r;
        r = c; 
        cout << "Second matrix columns: ";
        cin >> c;
        a2 = c;
        double m2[r][c] = {};
        if (inflag == true) {
            for (int i = 0; i < r; i++) {
                for (int j = 0; j < c; j++) {
                    cout << "Number in row " << i + 1 << " and column " << j + 1 << ": ";
                    cin >> b;
                    m2[i][j] = b;
                }
            }
        } else {
            for (int i = 0; i < r; i++) {
                for (int j = 0; j < c; j++) {
                   m2[i][j] = 1;
                }
            }
        }
        a[a1][a2];
        for (int i = 0; i < a1; i++) {
            for (int j = 0; j < a2; j++) {
                b = 0;
                for (int d = 0; d < b1; d++) {
                    b = b + (m[i][d] * m2[d][j]);
                }
                a[i][j] = b;
            }
        }
        for (int i = 0; i < a1; i++) {
            for (int j = 0; j < a2; j++) {
                cout << a[i][j] << "    ";
            }
        }
    } 
}

int main() {  //main file
    defmatrix();
    double m[r][c] = {};
    fillmatrix();
    matrixmult();
}

Thanks in advance!

  • `a[a1][a2]`: what is that ? And does your code really compile ? –  Mar 28 '22 at 19:33
  • 1
    `double m[1][1]` is a 1x1 array, a single value, and nothing will change this once the array has been defined. – user4581301 Mar 28 '22 at 19:36
  • @YvesDaoust That is defining the answer matrix for the multiplication. For matrix multiplication (ex. for multiplying a 3 x 2 and 2 x 1 matrix) the answer matrix is the size of the rows of the first matrix and the columns of the second matrix, or in this case 3 * 1. That command sets up the size of the answer matrix. – Christian de Frondeville Mar 28 '22 at 19:36
  • `double m[1][1]` Declares a *global* multidimensional array of size 1x1. `double m[r][c] = {};` declares a *local* variable length array, illegal in standard C++. Have you, by any chance, already been introduced to classes or dynamic memory allocation? – Bob__ Mar 28 '22 at 19:37
  • Recommendation: Write a few lines of code, just enough to do one simple thing. Compile and solve all errors and warnings. Test. Proceed to writing more code only when you are satisfied with the results you get across the full range of input values. When you write too much code at a time you often wind up having to untangle multiple bugs before you can solve them and often find fixing one bug makes you throw out a lot of code that depended on the previous buggy implementation. – user4581301 Mar 28 '22 at 19:40
  • Recommendation: Give variables and functions descriptive names. Good code comments itself and makes it easier to spot mistakes. – user4581301 Mar 28 '22 at 19:44
  • 1
    Recommendation: Avoid global variables. They make code easier to write, but they make it much more difficult to prove correct as any function anywhere in the code could contain a side effect that alters a global variable. – user4581301 Mar 28 '22 at 19:46
  • No, it does not. –  Mar 28 '22 at 20:13

2 Answers2

2

There is a problem in almost every line. It is easier to point out the good parts. The central for loop that calculates the matrix product seems OK. Most everything else needs to be thrown out and rewritten. This includes all declarations and all function interfaces.

Here's how I would start writing the program.

int main()
{
    int rows;
    int cols;
    int common_size; // columns in the matrix A and rows in the matrix B

    std::cout << "Enter number of rows in the result : ";
    std::cin  >> rows;
    std::cout << "Enter number of columns in the result : ";
    std::cin  >> cols;
    std::cout << "Enter the common size : ";
    std::cin  >> common_size;

Now we need to declare the matrices, but here comes a problem. One would naïvely want to write something like

    int a[rows][common_size], b[common_size][cols], c[rows][cols];

but alas, this is not legal C++. This declaration may work with your compiler, or it may not. There are several ways to declare the matrices correctly. The simplest one is to reserve a fixed amount of memory for each one, and just use a portion:

    int a[10][10], b[10][10], c[10][10];

Naturally you need to check that the sizes provided by the user do not exceed 10.

Another method is to use the C++ version of variable length array, called [vector]. The declaration of a will look something like this:

    std::vector<std::vector<int>> a(rows, std::vector<int>(common_size));

With the second method, there is no real limitation on the matrix sizes.

Neither method is adequate for any real software, but they are enough to complete your C++ exercise. Real software uses more advanced C++ concepts like classes to define matrices. Just something to be aware of.

I will not continue writing it at the same level of detail. This is your task. I will just show what the rest of main could look like.

    input_matrix(a, rows, common_size);
    input_matrix(b, common_size, cols);
    multiply_matrices(a, b, c, rows, common_size, cols);
    print_matrix(c, rows, cols);

Note function calls using arguments. Implement these functions, and you are good to go. Note, if you choose vectors, you will need to pass some arguments by reference. In this case you could also write the functions this way:

    input_matrix(a);
    input_matrix(b);
    c = multiply_matrices(a, b);
    print_matrix(c);

but this is a talk for another day.

n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243
0

Although you're asking to find mistakes in your code, as your task is interesting I decided to implement my own solution from scratch for you. Even if it is unacceptable answer, still it might be useful for educational purpose.

I made code in a form of class Matrix that supports multiplication operation between two matching matrices like A *= B;. As you did, I also implemented two methods .Input() and .Output() that correspondingly read matrix from std::cin and output to std::cout.

As a bonus there is also a method .At(i, j) that returns reference to element of a matrix with bounds checking, similar to std::vector.

Sure class can be extended to many other matrices operations (like addition A += B; or subtraction A -= B;), but I limited my class to only those things that you used in your original code.

To do what you wanted using my class is simple as few lines of code (that are located further in main() function of following code snippet):

Matrix A, B;
A.Input(); B.Input();
A *= B;
A.Output();

Full code:

Try it online!

#include <stdexcept>
#include <vector>
#include <sstream>
#include <iomanip>
#include <iostream>

#define ASSERT_MSG(cond, msg) { if (!(cond)) throw std::runtime_error("Assertion (" #cond ") failed at line " + std::to_string(__LINE__) + "! Msg: '" + std::string(msg) + "'."); }
#define ASSERT(cond) ASSERT_MSG(cond, "")

class Matrix {
public:
    using FloatT = double;
    Matrix(size_t rows = 0, size_t cols = 0)
        : rows_(rows), cols_(cols) {
        Clear();
    }
    Matrix & Clear() {
        m_.clear();
        m_.resize(rows_ * cols_);
        return *this;
    }
    size_t Rows() const { return rows_; }
    size_t Cols() const { return cols_; }
    Matrix & operator = (Matrix && other) {
        rows_ = other.rows_;
        cols_ = other.cols_;
        m_ = std::move(other.m_);
        other.rows_ = 0;
        other.cols_ = 0;
        return *this;
    }
    FloatT & operator() (size_t i, size_t j) {
        return m_[i * cols_ + j];
    }
    FloatT const & operator() (size_t i, size_t j) const {
        return const_cast<Matrix &>(*this)(i, j);
    }
    FloatT & At(size_t i, size_t j) {
        ASSERT_MSG(i < rows_ && j < cols_,
            "Matrix index (" + std::to_string(i) + ", " + std::to_string(j) +
            ") out of bounds (" + std::to_string(rows_) + ", " + std::to_string(cols_) + ")!");
        return (*this)(i, j);
    }
    FloatT const & At(size_t i, size_t j) const {
        return const_cast<Matrix &>(*this).At(i, j);
    }
    Matrix & operator *= (Matrix const & B) {
        Matrix const & A = *this;
        ASSERT_MSG(A.Cols() == B.Rows(),
            "Number of A.Cols " + std::to_string(A.Cols()) +
            " and B.Rows " + std::to_string(B.Rows()) + " don't match!");
        Matrix C(A.Rows(), B.Cols());

        for (size_t i = 0; i < A.Rows(); ++i)
            for (size_t j = 0; j < B.Cols(); ++j) {
                FloatT sum = 0;
                for (size_t k = 0; k < A.Cols(); ++k)
                    sum += A(i, k) * B(k, j);
                C(i, j) = sum;
            }

        *this = std::move(C);
        return *this;
    }
    Matrix & Input() {
        std::cout << "Enter number of rows and columns: ";
        std::cin >> rows_ >> cols_;
        Clear();
        std::cout << "Enter all matrix elements:" << std::endl;
        for (size_t i = 0; i < rows_; ++i)
            for (size_t j = 0; j < cols_; ++j)
                std::cin >> (*this)(i, j);
        return *this;
    }
    Matrix const & Output(size_t precision = 6) const {
        std::vector<std::vector<std::string>> cells(Rows(),
            std::vector<std::string>(Cols()));

        std::ostringstream ss;
        size_t max_len = 0;
        for (size_t i = 0; i < Rows(); ++i)
            for (size_t j = 0; j < Cols(); ++j) {
                ss.str("");
                ss << std::fixed << std::setprecision(precision)
                    << (*this)(i, j);
                cells[i][j] = ss.str();
                max_len = std::max(max_len, cells[i][j].size());
            }

        for (auto const & row: cells) {
            for (auto const & cell: row)
                std::cout << std::string(max_len - cell.size() + 1, ' ')
                    << cell;
            std::cout << std::endl;
        }

        return *this;
    }
private:
    size_t rows_ = 0, cols_ = 0;
    std::vector<FloatT> m_;
};

int main() {
    try {
        Matrix A, B;
        A.Input();
        B.Input();
        A *= B;
        A.Output();
        return 0;
    } catch (std::exception const & ex) {
        std::cout << "Exception: " << ex.what() << std::endl;
        return -1;
    }
}

Input:

Enter number of rows and columns: 3 2
Enter all matrix elements:
1.01 2.02
3.03 4.04
5.05 6.06

Enter number of rows and columns: 2 4
Enter all matrix elements:
 7.07  8.08  9.09 10.10
11.11 12.12 13.13 14.14

Output:

  29.582900  32.643200  35.703500  38.763800
  66.306500  73.447200  80.587900  87.728600
 103.030100 114.251200 125.472300 136.693400
Arty
  • 14,883
  • 6
  • 36
  • 69