1

下面是一个测试代码块,它应该在容差范围内确定 2 个双精度值的等效性。

double lhs_1 = 0.02;
double lhs_2 = 0.04;
double rhs = 0.03;
double tolerance = 0.01;

bool is_match_1 = (abs(lhs_1 - rhs) <= tolerance);
bool is_match_2 = (abs(lhs_2 - rhs) <= tolerance);

然而 is_match_2 结果为假,而 is_match_1 为真。我了解存储在计算机中的数字是离散值而不是连续的。有人可以分享解决方案吗?我宁愿在合理的范围内通过测试。有没有办法将 double 的值增加 1 以使其当前具有的任何精度(我不熟悉 double 的位布局)?因为我可能只是增加容差值来解决这个粒度问题。

编辑:

当这真正实现时,用户将定义输入和公差,所以我只是试图为他们输入的任何值提供预期的输出。

4

4 回答 4

2

在这里比较浮点数可能会有所帮助,或者为什么我的浮点比较不起作用?

于 2012-01-25T21:15:08.473 回答
2

不幸的是,选择容差没有“好的”规则。

您可以随意使用“机器 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 谓词。这使您无需将比较逻辑硬编码到您的算法中。

简而言之,没有一刀切的规则。你要根据问题来选择

于 2012-01-25T21:26:08.043 回答
0

具有容差的全部意义在于避免直接的相等检查。他们并不真正适用于双打,因为你刚刚学会了艰难的方式。在双打世界中,1+1 可能不等于 2(因为在内部它可能类似于 1.99999743)。

所以“差异等于公差”不是一个可靠的条件。公差应该比值之间的明显差异小 1-2 个数量级,而不是与预期差异相同。因此,如果您想检查 lhs_2 - rhs 是否等于 rhs - lhs_1 在公差范围内,以下检查会更好地为您服务:

fabs((lhs_2 - rhs) - (rhs - lhs_1)) < 0.0001
于 2012-01-25T21:24:30.837 回答
0

您的容差已经非常宽松 - 与您比较的数字相比,0.01 是巨大的。只需将其打开到 0.01000001 就可以了。

于 2012-01-25T21:25:46.493 回答