微小的数字代替零?

Rar*_*rge 5 c++ math double-precision

我一直在制作一个矩阵类(作为一个学习练习),并且在测试我的反函数时遇到了问题.

我输入一个任意矩阵:

2 1 1
1 2 1
1 1 2
Run Code Online (Sandbox Code Playgroud)

并得到它来计算逆,我得到了正确的结果:

0.75 -0.25 -0.25
-0.25 0.75 -0.25
-0.25 -0.25 0.75
Run Code Online (Sandbox Code Playgroud)

但当我尝试将两者相乘以确保我得到单位矩阵时,我得到:

1 5.5111512e-017 0
0 1 0
-1.11022302e-0.16 0 1
Run Code Online (Sandbox Code Playgroud)

为什么我得到这些结果?我会理解,如果我将奇怪的数字乘以我可以理解的一些舍入误差,但它所做的总和是:

2 * -0.25 + 1 * 0.75 + 1 * -0.25
Run Code Online (Sandbox Code Playgroud)

这显然是0,而不是5.111512e-017

如果我手动让它进行计算; 例如:

std::cout << (2 * -0.25 + 1 * 0.75 + 1 * -0.25) << "\n";
Run Code Online (Sandbox Code Playgroud)

我按预期得到0?

所有数字都表示为双打.这是我的多重过载:

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");
}
Run Code Online (Sandbox Code Playgroud)

和访问功能:

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];
}
Run Code Online (Sandbox Code Playgroud)

这是找到逆的函数:

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;
}
Run Code Online (Sandbox Code Playgroud)

Ear*_*rlz 11

请阅读本文:每个计算机科学家应该知道的关于浮点运算的内容

  • 如果对该数字进行的操作在一个点上创建了一个不完全可表示的中间结果,那么@Mark确实适用...这就是这个问题所遭受的问题 (7认同)
  • @Mark尝试在你的反演过程中打印掉你正在分割的`n` - 它在某个时刻是1.3333,这不会得到确切的结果 (3认同)
  • 如果您的数字完全可以用二进制表示,则不适用. (2认同)

n. *_* m. 9

你的倒置矩阵中有数字像0.250000000000000005,它们只是四舍五入显示,所以你看到很好的小圆数,如0.25.

  • 实际上,通过具有讽刺意味的巧合,0.25和0.75是0.01 .. 0.99序列中仅有的三个数字中的两个,它们在浮点格式中具有短而简单的精确表示.但是你的中间产品没有,这就是为什么你的计算只能精确到有限的精度. (3认同)
  • 啊,是的.我将流精度设置为高,-0.25s显示为-0.2499999999999999999999997谢谢:S (2认同)