方式一:
double a1 = ...;
double a2 = ....;
- fabs(a1-a2) < epsilon
- (fabs(a1-a2)/a2) < epsilon
有没有更好的方法?
谢谢
double a1 = ...;
double a2 = ....;
有没有更好的方法?
谢谢
这篇文章非常详细地回答了您的问题。您可能想跳到“Epsilon比较”一节。
个人而言,我在比较两个 double
时使用 std::nextafter
。这会使用一个值的最小 epsilon
(或最小 epsilon
的一个 factor
)。
bool nearly_equal(double a, double b)
{
return std::nextafter(a, std::numeric_limits<double>::lowest()) <= b
&& std::nextafter(a, std::numeric_limits<double>::max()) >= b;
}
bool nearly_equal(double a, double b, int factor /* a factor of epsilon */)
{
double min_a = a - (a - std::nextafter(a, std::numeric_limits<double>::lowest())) * factor;
double max_a = a + (std::nextafter(a, std::numeric_limits<double>::max()) - a) * factor;
return min_a <= b && max_a >= b;
}
nearly_equal(..,..,epsilon)
的结果不等于:fabs(right-left) < epsilon
? - Guillaume Parisnearly_equal(a, b, factor)
函数使用 factor * epsilon(a)
的容差来检查两个数的相等性。fabs(a-b) < epsilon
函数使用给定的 epsilon
容差来检查两个数的相等性。这两个表达式都很好,可以根据我们想要测试的内容进行使用。有时候,我们想要使用固定的 epsilon
来检查(即使找到正确的 epsilon
很困难:epsilon (1e20) = 16384
)。有时候,我们想要使用 epsilon
的因子来检查(即使找到正确的因子也很困难)。现在对于 fabs(a-b) < fabs(a) * epsilon
,最好使用 nearly_equal(a, b, epsilon)
函数来提高准确性。 - Daniel Laügt Epsilon的值根据双精度范围内的值而变化。如果您想使用自己的和固定的epsilon,我建议选择1
的epsilon。
fabs(a1-a2) < epsilon
这并不完美。如果a1
和a2
是由小数字进行计算得出的结果,那么epsilon将会很小。如果a1
和a2
是由大数字进行计算得出的结果,那么epsilon将会很大。
fabs((a1-a2)/a2) < epsilon
如果您希望按比例缩放epsilon
,那么这个方法就好一些了。但是,如果a2
等于0
,就会出现除以0
的情况。
fabs(a1-a2) < fabs(a2)*epsilon
这个已经好一些了。然而,如果 a2
等于 0
,这是不正确的。 fabs(a2)*epsilon
将等于 0
,并且两个值 a1=0.000000001
和 a2=0
的比较总是失败。
fabs(a1-a2) < max(fabs(a1), fabs(a2))*epsilon
这有一些改进。然而,这是不正确的,因为epsilon
在连续方面不成比例。使用IEEE编码,epsilon
在以2
为基数的离散方式上与一个值成比例。
fabs(a1-a2) < 2^((int)log2(max(fabs(a1), fabs(a2)))*epsilon
当a1
和a2
均不等于0
时,我认为这看起来是正确的。
一个通用方法的实现可以是:
bool nearly_equal(double a1, double a2, double epsilon)
{
if (a1 == 0 && a2 == 0)
return true;
return std::abs(a1 - a2) < epsilon * pow (2.0, static_cast<int> (std::log2(std::max(std::abs(a1), std::abs(a2)))));
}
fabs(a1-a2) < epsilon
比较a1
和a2
的绝对差异与容差epsilon
的大小。如果您事先知道缩放比例(例如,如果a2
实际上是一个常数),那么这可能是适当的,但如果您不知道a1
和a2
有多大,则通常应避免使用它。
第二种选项几乎计算出了相对差异,但存在一个错误;它实际上应该是:
fabs((a1-a2)/a2) < epsilon
请注意,除法运算在绝对值符号内部;否则,这个条件对于负数的a2
是无意义的。相对误差在大多数情况下更为准确,因为它更接近浮点舍入实际发生的方式,但有些情况下它并不适用,你需要使用绝对公差(通常是由于灾难性抵消)。有时你会看到以以下形式书写的相对误差界:
fabs(a1-a2) < fabs(a2)*epsilon
这通常会更有效,因为它避免了除法操作。
前者仅比较绝对值,而后者比较相对值。假设将epsilon设置为0.1
:如果a1
和a2
都较大,则这可能足够了。但是,如果两个值接近于零,第一种方法将认为大多数值相等。
这真的取决于您要处理的值的类型。如果使用稍微更具数学合理性的第二种情况,请务必考虑a2==0
的情况。
fabs(a1 - a2) / min(a1, a2)
和fabs(a1 - a2) / max(a1, a2)
,尤其是当你不知道这些数字是否接近零时,max(fabs(a1 - a2), fabs(a1 - a2) / a1)
就更加重要了)。 - Alexandre C.