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

发布于 2024-12-29 03:52:13 字数 513 浏览 4 评论 0原文

下面是一个测试代码块,旨在确定容差范围内 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 结果为 false,而 is_match_1 结果为 true。据我所知,计算机中存储的数字是离散值,而不是连续的。有人可以分享解决方案吗?我宁愿在合理范围内通过考试。有没有办法将双精度数的值增加 1,无论其当前的精度如何(我不熟悉双精度数的位布局)?因为我可能只是增加容差值以允许这种粒度问题。

编辑:

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

Below is a test block of code that is supposed to determine equivalency of 2 doubles within a tolerance.

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);

However is_match_2 turns out false where is_match_1 turns out true. I understand that numbers stored in the computer are discreet values and not continuous. Can someone share a solution? I would like to err on the side of passing the test within reason. Is there a way to increment the value of a double by 1 for whatever precision it currently has (I am not familiar with the bit layout of a double)? Because I might just increment the tolerance value to allow for this granularity issue.

EDIT:

When this is really implemented the user will define inputs and tolerances, so I'm just trying to give them an expected output for whatever values they enter.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

冷默言语 2025-01-05 03:52:13

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

您可以使用“机器 epsilon”

double epsilon = std::numeric_limits<double>::epsilon()

,它是最小值,与 1 相加,得到的结果与 1 不同。

我通常将公差写为 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 中的减号操作都会“吃掉” “ 1 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);
}

经常使用其他函数,例如 < code>std::pow(eps, Something),甚至-1 / std::log(eps)。这取决于我可以从问题中获得哪些先验信息,以及我预期的错误是什么。

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

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

Unfortunately, there are no "good" rules for choosing the tolerance.

You have at your disposal the "machine epsilon"

double epsilon = std::numeric_limits<double>::epsilon()

which is the smallest value which, added to one, gives a result different from one.

I usually write my tolerances as a function of epsilon. There are no good rules, but for instance comparing like this

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);
}

works well in many cases. You can adjust the 1024, I like powers of two, but you might not. The actual value you choose is problem-dependent. Epsilon for doubles is around 10^-16, so 1024 is quite small, and you will need a bigger number in many cases (virtually any operation, including the minus operation inside fuzzy_equals will "eat" one epsilon -- they can cancel out, but on average, n operations mean sqrt(n) * epsilon precision, so 1024 corresponds to the expected precision after one million operations).

In other cases, where the precision is not as good, for instance when testing the minimum of a function against a known value (minima are usually only determined up to sqrt(eps) accuracy), I use

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);
}

I often use other functions, like std::pow(eps, something), or even -1 / std::log(eps). This depends on what prior information I can derive from the problem, and what is the error I expect.

When it comes to code structure, I use a functional approach and pass a comparer to my algorithms, a bit like STL predicates. This enables you not to hardcode the logic of comparing into your algorithms.

In short, there is no one-size-fits-all rule. You have to choose depending on the problem

迟到的我 2025-01-05 03:52:13

容忍的全部意义在于避免直接的相等性检查。它们并不真正适用于双打,因为你刚刚经历了惨痛的教训。在双打世界中,1+1 可能不等于 2(因为在内部它可能类似于 1.99999743)。

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

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

The whole point of having a tolerance is avoiding straightforward equality checks. They don't really work for doubles, as you've just learned the hard way. In the world of doubles, 1+1 may not equal 2 (as internally it might be something like 1.99999743).

So "Difference equals tolerance" is not a reliable condition. Tolerance should be 1-2 orders of magnitude smaller than the sensible difference between values, as opposed as same as expected difference. So if you wanna check if lhs_2 - rhs is equal to rhs - lhs_1 within tolerance, the following check would serve you better:

fabs((lhs_2 - rhs) - (rhs - lhs_1)) < 0.0001
忱杏 2025-01-05 03:52:13

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

Your tolerance is already very loose - 0.01 is HUGE compared to the numbers you're comparing. Just open it up to 0.01000001 and you'll be fine.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文