不幸的是,选择容差没有“好的”规则。
您可以随意使用“机器 epsilon”
double epsilon = std::numeric_limits<double>::epsilon()
这是最小的值,加一,得出的结果不同于一。
我通常将我的公差写成 epsilon 的函数。没有好的规则,但例如像这样比较
bool fuzzy_equals(double a, double b)
{
static const double eps = std::numeric_limits<double>::epsilon();
return std::fabs(b - a) < 1024 * eps * std::max(a, b);
}
在许多情况下效果很好。您可以调整 1024,我喜欢 2 的幂,但您可能不会。您选择的实际值取决于问题。双打的 Epsilon 约为 10^-16,因此 1024 非常小,在许多情况下您需要更大的数字(实际上任何操作,包括内部的减号操作fuzzy_equals
都会“吃掉”一个 epsilon - 它们可以抵消,但是平均而言,n
操作意味着sqrt(n) * epsilon
精度,因此 1024 对应于一百万次操作后的预期精度)。
在其他情况下,精度不太好,例如,在针对已知值测试函数的最小值时(最小值通常仅确定为 sqrt(eps) 精度),我使用
bool fuzzy_equals2(double a, double b)
{
static const double eps = std::numeric_limits<double>::epsilon();
return std::fabs(b - a) < 1024 * std::sqrt(eps) * std::max(a, b);
}
我经常使用其他功能,例如std::pow(eps, something)
,甚至-1 / std::log(eps)
. 这取决于我可以从问题中获得哪些先验信息,以及我期望的错误是什么。
在代码结构方面,我使用函数式方法并将比较器传递给我的算法,有点像 STL 谓词。这使您无需将比较逻辑硬编码到您的算法中。
简而言之,没有一刀切的规则。你要根据问题来选择