'assert' 函数:奇怪的行为

'assert' function : weird behavior

我正在使用 Qt Creator 在 C++ 中开发 Windows 7。 我也使用库 OpenCV

我遇到了函数 assert 的一个奇怪问题。 我开发了一个函数,可以对图像进行线性变换以获得更好的对比度,可以指定我们要处理的图像的区域 (x1,x2,y1,y2) 以及公差 (Tolmin和 Tolmax) :我们要忽略的直方图的百分位数。

我需要确保:

我使用了函数assert

我的代码如下:

void equalizeHist_16U_linear(Mat &img, float Tolmin, float Tolmax, int x1, int x2, int y1, int y2)
{
    assert(img.channels() == 1);
    assert(img.type() == CV_16U);
    assert(x1>=0 && y1>=0 && x2>=x1 && y2>=y1);
    assert(y2<img.rows && x2<img.cols);
    assert(Tolmin>0 && Tolmax>0);
    assert(Tolmin+Tolmax != 1.0);

    ## code ##
    ...

}

前五个 assert 工作正常,但最后一个 assert(Tolmin+Tolmax != 1.0) 不工作。即使在 Tolmin+Tolmax=1 时,assert 也不会停止执行,因此程序会崩溃(除以 0)。 可以肯定的是,我在 assert 调用之前打印了 Tolmin+Tolmax

void equalizeHist_16U_linear(Mat &img, float Tolmin, float Tolmax, int x1, int x2, int y1, int y2)
{
    cout << endl << "Tolmin+Tolmax = " << Tolmin+Tolmax << endl;

    assert(img.channels() == 1);
    assert(img.type() == CV_16U);
    assert(x1>=0 && y1>=0 && x2>=x1 && y2>=y1);
    assert(y2<img.rows && x2<img.cols);
    assert(Tolmin>0 && Tolmax>0);
    assert(Tolmin+Tolmax != 1.0);

    ## code ##
    ...

}

显示"Tolmin+Tolmax=1",此时assert停止了程序的执行!

怎么可能?为什么 Tolmin+Tolmax 的显示使 assert 起作用?

我尝试添加以下内容:

void equalizeHist_16U_linear(Mat &img, float Tolmin, float Tolmax, int x1, int x2, int y1, int y2)
{
    float sum = Tolmin+Tolmax;

    assert(img.channels() == 1);
    assert(img.type() == CV_16U);
    assert(x1>=0 && y1>=0 && x2>=x1 && y2>=y1);
    assert(y2<img.rows && x2<img.cols);
    assert(Tolmin>0 && Tolmax>0);*/
    assert(sum != 1.0);

    ## code ##
    ...

}

但是也不行。

我的猜测是您的代码中使用的值(如 Tolmin 或 Tolmax)无法以机器的数字格式表示。 我是什么意思?好吧,有些数字您很容易想到,但计算机无法按原样存储它们。计算机必须以有限位二进制格式存储数字,因此某些数字将不会完全按原样存储。这样想,我们可以想到数字(2/3)对吧?但是当我们必须用有限位数的十进制格式表示这个数字时,我们必须像这样四舍五入:0.66666667 因此我们不能准确地写出这个数字。这种情况也可能在机器中产生,例如我们不能将数字 0.2 以二进制格式存储在机器中,因为它的二进制代表数字是无限的,机器只会将这些数字四舍五入到它可以存储的最接近的数字。 在您的情况下,您正在将转换为二进制的数字与绝对值 (1.0) 进行比较,并且即使它们彼此非常接近,逻辑上它们也不相同。所以我的猜测是 Tolmin 和 Tolmax 的总和将非常接近 1.0 但不完全是 1.0。那么解决方案是什么?您应该检查 sum 是否已通过任意间隔到 1.0。像这样:

assert(abs(sum - 1.0) > 0.0001)

更新: 我无法在打印时重现您报告的行为,无论是使用 gcc 还是 MSC++,所以我只能做一个微弱的猜测。当您构建代码时,编译器将在优化阶段对其进行修改。您要在两个地方计算 Tolmin+Tolmax,首先在 cout 中,其次在 assert 中,因此编译器将推断此计算可以完成一次,然后重用它的结果。由于 cout 将你命令显示的数字进行改造,而该值非常接近 1,它会四舍五入得到准确的值 1,然后结果将在 assert 中重复使用。是否如此,我无法确认,因为我无法重现报告的行为。