将浮点数与零进行比较

2022-01-09 00:00:00 floating-point c++

C++ FAQ lite "[29.17] 为什么我的浮点比较有效吗?" 推荐这种相等性测试:

The C++ FAQ lite "[29.17] Why doesn't my floating-point comparison work?" recommends this equality test:

#include <cmath>  /* for std::abs(double) */

inline bool isEqual(double x, double y)
{
  const double epsilon = /* some small number such as 1e-5 */;
  return std::abs(x - y) <= epsilon * std::abs(x);
  // see Knuth section 4.2.2 pages 217-218
}

  1. 这是否正确,这意味着唯一等于零的数字是 +0-0?
  2. 在测试零或者更确切地说是像 |x| 这样的测试时,是否也应该使用此功能?<ε?

更新

正如 Daniel Daranas 所指出的,该函数可能最好称为 isNearlyEqual(这是我关心的情况).

As pointed out by Daniel Daranas the function should probably better be called isNearlyEqual (which is the case I care about).

有人指出 "比较浮动点数",我想更突出地分享.

Someone pointed out "Comparing Floating Point Numbers", which I want to share more prominently.

推荐答案

你的观察是正确的.

如果 x == 0.0,则 abs(x) * epsilon 为零,您正在测试 abs(y) <= 0.0.

If x == 0.0, then abs(x) * epsilon is zero and you're testing whether abs(y) <= 0.0.

如果 y == 0.0 那么你正在测试 abs(x) <= abs(x) * epsilon 这意味着 epsilon >=1(不是)或 x == 0.0.

If y == 0.0 then you're testing abs(x) <= abs(x) * epsilon which means either epsilon >= 1 (it isn't) or x == 0.0.

所以 is_equal(val, 0.0)is_equal(0.0, val) 都是没有意义的,你可以说 val == 0.0.如果您只想接受 exactly +0.0-0.0.

So either is_equal(val, 0.0) or is_equal(0.0, val) would be pointless, and you could just say val == 0.0. If you want to only accept exactly +0.0 and -0.0.

在这种情况下,FAQ 的建议是有限的.没有一刀切"的浮点比较.您必须考虑变量的语义、可接受的值范围以及计算引入的误差幅度.甚至常见问题解答都提到了一个警告,说这个函数通常不是问题当 x 和 y 的大小明显大于 epsilon 时,但你的里程可能会有所不同".

The FAQ's recommendation in this case is of limited utility. There is no "one size fits all" floating-point comparison. You have to think about the semantics of your variables, the acceptable range of values, and the magnitude of error introduced by your computations. Even the FAQ mentions a caveat, saying this function is not usually a problem "when the magnitudes of x and y are significantly larger than epsilon, but your mileage may vary".

相关文章