影响数值比较的双粒度c ++

时间:2012-01-25 21:09:54

标签: c++ bit double-precision granularity

下面是一个代码测试块,用于确定容差范围内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_1结果为真,则is_match_2结果为false。我知道存储在计算机中的数字是谨慎的值而不是连续的。有人可以共享解决方案吗?我想在合理的范围内通过测试。有没有办法将double的值递增1,无论它目前的精度如何(我不熟悉double的位布局)?因为我可能只是增加容差值以允许这种粒度问题。

修改

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

4 个答案:

答案 0 :(得分:2)

答案 1 :(得分:2)

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

您可随意使用“machine 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,我喜欢两个的力量,但你可能不会。您选择的实际值与问题有关。 Epsilon for double是大约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)。这取决于我可以从问题中得出什么先验信息,以及我期望的错误是什么。

在代码结构方面,我使用函数方法并将 comparer 传递给我的算法,有点像STL谓词。这使您无需将比较逻辑硬编码到算法中。

简而言之,没有一刀切的规则。您必须根据问题选择

答案 2 :(得分:0)

容忍的全部意义在于避免直接的平等检查。他们并没有真正为双打工作,因为你刚刚学到了很多困难。在双打世界中,1 + 1可能不等于2(内部可能是1.99999743)。

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

fabs((lhs_2 - rhs) - (rhs - lhs_1)) < 0.0001

答案 3 :(得分:0)

你的耐受性已经非常宽松 - 与你所比较的数字相比,0.01是巨大的。只需将其打开至0.01000001就可以了。