【发布时间】:2015-01-05 21:03:30
【问题描述】:
我很好奇在用标量(双精度)除矩阵(双值)时是否有人能够得到正确的结果。当我试图追踪 MATLAB 中的算法与 C++ 中重现的算法之间的一些不一致的来源时,我注意到 OpenCV 没有给出正确的(嗯,“准确的”)结果。这是我看到的问题的一个最小示例:
cv::Mat some_matrix(1, 1, CV_64FC1, cv::Scalar::all(95));
cv::Mat some_matrix_div = some_matrix / 235.0;
printf(
"Expected: %.53g\n"
"OpenCV : %.53g\n",
some_matrix.at<double>(0,0) / 235.0,
some_matrix_div.at<double>(0,0) );
运行后我看到
Expected: 0.40425531914893614304773450385255273431539535522460938
OpenCV : 0.404255319148936198558885735110379755496978759765625
第一个是值应该是什么(如果您在 C++ 或 MATLAB 中执行 95/235 的双精度除法,您将得到什么),但第二个是 OpenCV 在使用除法运算符时产生的结果。我尝试在 OpenCV 源代码中追踪问题,但矩阵运算有点复杂,目前我没有很多时间来遍历它,所以我想知道是否有其他人遇到过这个问题并且知道解决方法吗?
编辑
我会补充一些说明。
首先,我知道双精度不是精确的数字表示。我所说的“精确”(为什么用引号引起来)的意思是完全执行的双重除法(例如打印 95.0/235.0 的结果)与 OpenCV 在将矩阵除以标量时所做的并不完全相同,尽管如此矩阵中的值确实存储为双精度,标量也确实被视为双精度。人们会期望这两个结果应该是相同的。也就是说,如果我将一个双精度数除以另一个双精度数,结果应该与 OpenCV 双精度矩阵除以一个双精度标量相同。
我也已经尝试在代码中将所有数字常量显式转换为双精度数,但没有成功。
虽然在这种情况下确实如此,但差异相对较小 (e^-16),但我不确定随着时间的推移,这可能会如何复合以产生越来越大的错误。这是一个问题。另一个更多的是一个小烦恼,一个误解为什么 OpenCV 没有做人们直觉期望它做的事情。最后它可能不会引起任何问题,但如果可以避免奇怪的行为,我显然更喜欢这样做,特别是因为它使计算与 MATLAB 结果的预期结果不匹配时变得不清楚,因为计算奇怪或因为一个实际的算法实现问题(这是我假设的)。
希望这更清楚。
【问题讨论】:
-
您的结果不“准确”,因为
double不准确。 en.cppreference.com/w/cpp/types/numeric_limits/is_exact -
您是否使用 -fast-math 或类似的东西进行编译?
-
两个结果重合为16位,为双精度。差异真的相关吗? – DrewDormann,PaulMcKenzie,是的,他们并不准确。问题是,为什么它们不完全一样?
-
@Borgleader 有趣的是,如果我使用 --fast-math 编译,OpenCV 结果现在确实匹配!虽然我不太明白为什么 OpenCV 结果发生了变化,而不是我执行的直接计算。它必须影响 OpenCV 中的内联操作。你能写一个答案让我接受吗?这似乎确实解决了问题。
-
啊,我搞混了。确实不是 OpenCV 结果发生了变化。这更有意义。所以我的 OpenCV 库必须用 --fast-math 编译。
标签: c++ matlab opencv division