Tiny numbers in place of zero?

…衆ロ難τιáo~ 提交于 2019-12-23 09:47:31

问题


I have been making a matrix class (as a learning exercise) and I have come across and issue whilst testing my inverse function.

I input a arbitrary matrix as such:

2 1 1
1 2 1
1 1 2

And got it to calculate the inverse and I got the correct result:

0.75 -0.25 -0.25
-0.25 0.75 -0.25
-0.25 -0.25 0.75

But when I tried multiplying the two together to make sure I got the identity matrix I get:

1 5.5111512e-017 0
0 1 0
-1.11022302e-0.16 0 1

Why am I getting these results? I would understand if I was multiplying weird numbers where I could understand some rounding errors but the sum it's doing is:

2 * -0.25 + 1 * 0.75 + 1 * -0.25

which is clearly 0, not 5.111512e-017

If I manually get it to do the calculation; eg:

std::cout << (2 * -0.25 + 1 * 0.75 + 1 * -0.25) << "\n";

I get 0 as expected?

All the numbers are represented as doubles. Here's my multiplcation overload:

Matrix operator*(const Matrix& A, const Matrix& B)
{
    if(A.get_cols() == B.get_rows())
    {
        Matrix temp(A.get_rows(), B.get_cols());
        for(unsigned m = 0; m < temp.get_rows(); ++m)
        {
            for(unsigned n = 0; n < temp.get_cols(); ++n)
            {
                for(unsigned i = 0; i < temp.get_cols(); ++i)
                {
                    temp(m, n) += A(m, i) * B(i, n);
                }
            }
        }

        return temp;
    }

    throw std::runtime_error("Bad Matrix Multiplication");
}

and the access functions:

double& Matrix::operator()(unsigned r, unsigned c)
{
    return data[cols * r + c];
}

double Matrix::operator()(unsigned r, unsigned c) const
{
    return data[cols * r + c];
}

Here's the function to find the inverse:

Matrix Inverse(Matrix& M)
{
    if(M.rows != M.cols)
    {
        throw std::runtime_error("Matrix is not square");
    }

    int r = 0;
    int c = 0;
    Matrix augment(M.rows, M.cols*2);
    augment.copy(M);

    for(r = 0; r < M.rows; ++r)
    {
        for(c = M.cols; c < M.cols * 2; ++c)
        {
            augment(r, c) = (r == (c - M.cols) ? 1.0 : 0.0);
        }
    }

    for(int R = 0; R < augment.rows; ++R)
    {
        double n = augment(R, R);
        for(c = 0; c < augment.cols; ++c)
        {
            augment(R, c) /= n;
        }

        for(r = 0; r < augment.rows; ++r)
        {
            if(r == R) { continue; }
            double a = augment(r, R);

            for(c = 0; c < augment.cols; ++c)
            {
                augment(r, c) -= a * augment(R, c);
            }
        }
    }

    Matrix inverse(M.rows, M.cols);
    for(r = 0; r < M.rows; ++r)
    {
        for(c = M.cols; c < M.cols * 2; ++c)
        {
            inverse(r, c - M.cols) = augment(r, c);
        }
    }

    return inverse;
}

回答1:


You've got numbers like 0.250000000000000005 in your inverted matrix, they're just rounded for display so you see nice little round numbers like 0.25.




回答2:


Please read this paper: What Every Computer Scientist Should Know About Floating-Point Arithmetic




回答3:


You shouldn't have any problems with these numbers, since with this particular matrix the inverse is all power of 2's and may be represented accurately. In general, operations on floating point numbers introduce small errors that may accumulate and the results may be surprising.

In your case, I'm pretty sure the inverse is inaccurate and you're just displaying the first few digits. I.e., it isn't exactly 0.25 (=1/4), 0.75 (=3/4), etc.




回答4:


You're always going to run into floating point rounding errors like this, especially when working with numbers that do not have exact binary representations (i.e., your numbers are not equal to 2^(N) or 1/(2^N), where N is some integer value).

That being said, there are a number of ways to increase the precision of your results, and you may want to-do a google search for numerically stable gaussian elimination algorithms using fixed-precision floating point values.

You can also, if you are willing to take a speed hit, incorporate an inifinite-precision math library that uses rational numbers, and if you take that choice, just avoid the use of roots which can create irrational numbers. There are a number of libraries out there that can help you with the use of rational numbers, such as GMP. You can also make a rational class yourself, although beware it's relatively easy to overflow the results of multiple math operations if you are only using unsigned 64-bit values along with an extra sign-flag variable for the components of your rational numbers. That's where GMP, with it's unlimited-length integer string objects comes in handy.




回答5:


It's just simple floating point error. Even doubles on computers aren't 100% accurate. There just is no way to 100% accurately represent a base-10 decimal number in binary with a finite number of bits.



来源:https://stackoverflow.com/questions/6230869/tiny-numbers-in-place-of-zero

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!