快速log2(float x)实现C ++

时间:2012-02-23 11:07:50

标签: c++ performance

我需要在C ++中实现非常快速的log2(float x)函数。

我发现了一个非常有趣的实现(而且非常快!)

#include <intrin.h>

inline unsigned long log2(int x)
{
    unsigned long y;
    _BitScanReverse(&y, x);
    return y;
}

但是这个函数仅适用于输入中的整数值。

问题:有没有办法将此功能转换为 double 类型的输入变量?

UPD

我发现了这个实现:

typedef unsigned long uint32;
typedef long int32;   
static inline int32 ilog2(float x)
{
    uint32 ix = (uint32&)x;
    uint32 exp = (ix >> 23) & 0xFF;
    int32 log2 = int32(exp) - 127;

    return log2;
}

比前一个示例快得多,但输出是无符号类型。

是否可以使此功能返回 double 类型?

提前致谢!

8 个答案:

答案 0 :(得分:6)

如果你只需要对数的整数部分,那么你可以直接从浮点数中提取它。

便携地:

#include <cmath>

int log2_fast(double d) {
    int result;
    std::frexp(d, &result);
    return result-1;
}

可能更快,但依赖于未指定和未定义的行为:

int log2_evil(double d) {
    return ((reinterpret_cast<unsigned long long&>(d) >> 52) & 0x7ff) - 1023;
}

答案 1 :(得分:5)

MSVC + GCC兼容版本,提供XX.XXXXXXX + -0.0054545

float mFast_Log2(float val) {
    union { float val; int32_t x; } u = { val };
    register float log_2 = (float)(((u.x >> 23) & 255) - 128);              
    u.x   &= ~(255 << 23);
    u.x   += 127 << 23;
    log_2 += ((-0.3358287811f) * u.val + 2.0f) * u.val  -0.65871759316667f; 
    return (log_2);
} 

答案 2 :(得分:4)

编辑:有关更好的版本,请参阅下面评论中的“按作业链接”链接。


Fast log() function(约快5倍)

也许对你感兴趣。代码在这里工作;但它并非无限精确。由于代码在网页上被破坏(&gt;已被删除),我将在此处发布:

inline float fast_log2 (float val)
{
   int * const    exp_ptr = reinterpret_cast <int *> (&val);
   int            x = *exp_ptr;
   const int      log_2 = ((x >> 23) & 255) - 128;
   x &= ~(255 << 23);
   x += 127 << 23;
   *exp_ptr = x;

   val = ((-1.0f/3) * val + 2) * val - 2.0f/3;   // (1)

   return (val + log_2);
} 

inline float fast_log (const float &val)
{
   return (fast_log2 (val) * 0.69314718f);
}

答案 3 :(得分:2)

您可以查看this implementation,但是:

  • 它可能无法在某些平台上运行
  • 可能无法击败std :: log

答案 4 :(得分:2)

C ++ 11将std::log2添加到<cmath>

答案 5 :(得分:0)

这是对第一个答案的改进,它不依赖于IEEE实现,尽管我认为它只能在IEEE机器上快速frexp()基本上是无成本函数。

不是丢弃frexp返回的分数,而是可以使用它进行线性插值。如果积分值为正,则分数值介于0.5和1.0之间,因此我们将介于0.0和1.0之间并将其添加到指数中。

在实践中,看起来这种快速评估对于大约5-10%是好的,总是返回一个稍低的值。我确信通过调整2*缩放因子可以使它更好。

#include <cmath>

double log2_fast(double d) {
    int exponent;
    double fraction = std::frexp(d, &exponent);
    return (result-1) + 2* (fraction - 0.5);
}

您可以通过以下方式验证这是合理的快速近似值:

#include <cmath>

int main()
{
   for(double x=0.001;x<1000;x+=0.1)
   {
      std::cout << x << " " << std::log2(x) << " " << log2_fast(x) << "\n";
   }
}

答案 6 :(得分:-2)

这个函数不是C ++,它是MSVC ++特有的。此外,我非常怀疑任何这样的内在函数存在。如果他们这样做,标准函数将被配置为使用它。所以只需调用标准提供的库。

答案 7 :(得分:-2)

不,但是如果你只需要结果的组成部分并且不坚持可移植性,那就更快了。因为您只需要提取浮动的指数部分!