高效的浮点比较

efficient floating point comparison

本文关键字:比较 高效      更新时间:2023-10-16

嗨,我有一个函数定义为

int compareAB(float A, float B)  
{  
    if(A > B) 
    {  
        return 1;  
    }
    else if(A < B)
    {
        return -1;  
    }  
    else
    {
        return 0;
    }
}

上面的函数在我的项目中花费了太多的时间来比较,因为我可以从性能工具中看到。我能改进它的运行时间吗?

我认为一种方法是取两个数的差,然后与0比较。

谢谢,

编辑:这个函数被用于排序比较函数,用于维护一个搜索树数据结构。现在也更正了返回值。

这看起来像是一种试图规避"不应该对浮点进行相等性比较"规则的人为方式。比较不等号和比较等号没有太大的不同,因为在这两种情况下,你都隐含地依赖于浮点精度。最后的else语句是隐式的A == b

通常的习惯用法是if (::fabs(A - B) < e),其中e是一些公差,尽管在您的情况下您不需要::fabs

如果你想要不同的正、负和等结果(在计算精度的限制内),那么执行类似

的操作
if (A - B > e){
    return 0;
} else if (A - B < -e){
    return 1;
} else {
    return -1;
}

最好的情况是将e设置为std::numeric_limits<double>::epsilon()。实际值取决于为了到达A和b而执行的计算步骤的数量。

至于速度,很不幸的是:我看不出这是瓶颈或者运行得更快。

这里的一个问题是返回值完全不正确。第一个返回值应该是1,第二个应该是-1,第三个应该是0。如果您使用此方法进行排序,则排序可能不稳定,这会增加其运行时间。

原则上您可以只返回a-b,除非您要处理NaN s。

浮点比较可能比普通整数比较更昂贵,特别是在没有专用浮点硬件的情况下。

幸运的是,在某些情况下,您可以在比较浮点数时使用整数比较:

1)这仅在使用IEEE754浮点格式时有效。2)不支持NaN:s.

访问底层表示是定义的行为,因为C语言没有指定它使用哪种浮点格式。

无论如何,诀窍是它只在浮点数具有相同符号的情况下才有效。在这种情况下,比较两个浮点数的整数表示形式是比较浮点数本身的

我没有对下面的代码进行性能测试,但很可能它比原始代码快。请让我知道性能增益,如果有的话!

int compareAB(float a_orig, float b_orig)
{
  /* Get the underlying representation of A and B. */
  long a = *(unsigned long *)&a_orig;
  long b = *(unsigned long *)&b_orig;
  if (a < 0)
  {
    if (b < 0)
    {
      /* Both are negative. The order of the integer representation is
       * the OPPOSITE of the order of the floating-point value. */
      if (a > b)
      {
        return -1;
      }
      else if (a < b)
      {
        return 1;
      }
      else
      {
        return 0;
      }
    }
    else
    {
      /* A is negative, B isn't => A is smaller. */
      return -1;
    }
  }
  else if (b < 0)
  {
    /* B is negative, A isn't => B is smaller. */
    return 1;
  }
  else
  {
    /* Both are positive. */
    if (a > b)
    {
      return 1;
    }
    else if (a < b)
    {
      return -1;
    }
    else
    {
      return 0;
    }
  }
}

你可以这样测试:

#include <stdio.h>
float values[] = {-100.0F,
                  -50.0F,
                  0.0F,
                  50.0F,
                  100.0F };
void test(float a, float b)
{
  const char * p = 0;
  printf("%f is ", a);
  switch (compareAB(a, b))
  {
  case -1: p = "smaller than"; break;
  case  0: p = "equal to"; break;
  case  1: p = "greater than"; break;
  }
  printf("%s %fn", p, b);
}
int main(void)
{
  int i;
  for (i = 0; i < sizeof(values)/sizeof(values[0]); ++i)
  {
    int j;
    for (j = 0; j < sizeof(values)/sizeof(values[0]); ++j)
    {
      test(values[i], values[j]);
    }
  }
}

它给出与使用原始代码时相同的输出,即:

-100.000000 is equal to -100.000000
-100.000000 is smaller than -50.000000
-100.000000 is smaller than 0.000000
-100.000000 is smaller than 50.000000
-100.000000 is smaller than 100.000000
-50.000000 is greater than -100.000000
-50.000000 is equal to -50.000000
-50.000000 is smaller than 0.000000
-50.000000 is smaller than 50.000000
-50.000000 is smaller than 100.000000
0.000000 is greater than -100.000000
0.000000 is greater than -50.000000
0.000000 is equal to 0.000000
0.000000 is smaller than 50.000000
0.000000 is smaller than 100.000000
50.000000 is greater than -100.000000
50.000000 is greater than -50.000000
50.000000 is greater than 0.000000
50.000000 is equal to 50.000000
50.000000 is smaller than 100.000000
100.000000 is greater than -100.000000
100.000000 is greater than -50.000000
100.000000 is greater than 0.000000
100.000000 is greater than 50.000000
100.000000 is equal to 100.000000