进行浮动和双重比较的最有效方法是什么?

≯℡__Kan透↙ 提交于 2020-02-26 14:32:09

比较两个double或两个float值的最有效方法是什么?

简单地这样做是不正确的:

bool CompareDoubles1 (double A, double B)
{
   return A == B;
}

但是类似:

bool CompareDoubles2 (double A, double B) 
{
   diff = A - B;
   return (diff < EPSILON) && (-diff < EPSILON);
}

似乎是废物处理。

有人知道更聪明的浮点比较器吗?


#1楼

实际上,在数字软件中,您需要检查两个浮点数是否完全相等。 我在类似的问题上发布了

https://stackoverflow.com/a/10973098/1447411

因此,您不能说“ CompareDoubles1”通常是错误的。


#2楼

意识到这是一个古老的话题,但是本文是我发现的比较浮点数最直接的文章之一,如果您想探索更多,它也提供了更详细的参考,并且主要站点涵盖了所有问题处理浮点数《浮点指南:比较》

我们可以在“ 浮点公差 ”中找到一些更实用的文章,并注意到存在绝对公差测试,在C ++中可以归结为:

bool absoluteToleranceCompare(double x, double y)
{
    return std::fabs(x - y) <= std::numeric_limits<double>::epsilon() ;
}

相对公差测试:

bool relativeToleranceCompare(double x, double y)
{
    double maxXY = std::max( std::fabs(x) , std::fabs(y) ) ;
    return std::fabs(x - y) <= std::numeric_limits<double>::epsilon()*maxXY ;
}

本文指出,当xy较大时,绝对测试将失败,而当它们相对较小时,则相对测试将失败。 假设他的绝对公差和相对公差相同,那么组合测试将如下所示:

bool combinedToleranceCompare(double x, double y)
{
    double maxXYOne = std::max( { 1.0, std::fabs(x) , std::fabs(y) } ) ;

    return std::fabs(x - y) <= std::numeric_limits<double>::epsilon()*maxXYOne ;
}

#3楼

与大多数人所做的事情(包括在游戏编程中)相比,与ε值进行比较。

您应该对实现进行一些更改:

bool AreSame(double a, double b)
{
    return fabs(a - b) < EPSILON;
}

编辑:克里斯特(Christer)在最近的博客文章中添加了有关此主题的大量信息。 请享用。


#4楼

您编写的代码存在错误:

return (diff < EPSILON) && (-diff > EPSILON);

正确的代码是:

return (diff < EPSILON) && (diff > -EPSILON);

(...是的,这是不同的)

我不知道晶圆厂在某些情况下是否不会让您失去懒惰的评估。 我会说这取决于编译器。 您可能要同时尝试两者。 如果它们平均相等,则采用fab实施。

如果您有关于两个浮点数中哪个比另一个浮点数更大的信息,则可以按比较的顺序进行操作,以更好地利用延迟评估。

最后,通过内联此函数可能会获得更好的结果。 虽然不太可能改善...

编辑:OJ,感谢您更正您的代码。 我因此删除了我的评论


#5楼

有关更深入的方法,请阅读比较浮点数 。 这是该链接的代码片段:

// Usable AlmostEqual function    
bool AlmostEqual2sComplement(float A, float B, int maxUlps)    
{    
    // Make sure maxUlps is non-negative and small enough that the    
    // default NAN won't compare as equal to anything.    
    assert(maxUlps > 0 && maxUlps < 4 * 1024 * 1024);    
    int aInt = *(int*)&A;    
    // Make aInt lexicographically ordered as a twos-complement int    
    if (aInt < 0)    
        aInt = 0x80000000 - aInt;    
    // Make bInt lexicographically ordered as a twos-complement int    
    int bInt = *(int*)&B;    
    if (bInt < 0)    
        bInt = 0x80000000 - bInt;    
    int intDiff = abs(aInt - bInt);    
    if (intDiff <= maxUlps)    
        return true;    
    return false;    
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!