找出数字范围内的2个范围的幂? (在C中)

时间:2012-08-14 18:07:32

标签: c exponent

如是否落在2 ^ 3 - 2 ^ 4,2 ^ 4 - 2 ^ 5等内。返回的数字将是EXPONENT本身(减去偏移量)。

如何尽可能快速有效地完成这项工作?这个函数将在一个非常依赖于速度的程序中被调用。这是我当前的代码,但它使用for循环效率太低了。

static inline size_t getIndex(size_t numOfBytes)
{
    int i = 3;
    for (; i < 32; i++) 
    {
        if (numOfBytes < (1 << i)) 
            return i - OFFSET;
    }
    return (NUM_OF_BUCKETS - 1);
}

非常感谢!

7 个答案:

答案 0 :(得分:9)

据我所知,你所追求的只是log 2 n )。

如果您的目标体系结构具有可以执行此操作的指令,则可能值得作弊并使用一些内联汇编。有关硬件支持的大量讨论和信息,请参阅“{first}”上的Wikipedia entry

答案 1 :(得分:5)

这样做的一种方法是找到设置为1的最高位。我试着想一想这是否有效,因为在最坏的情况下你仍然需要进行n次检查。

也许你可以做二进制搜索样式,你检查它是否大于2 ^ 16,如果是,检查它是否大于2 ^ 24(假设这里是32位),如果没有,那么检查它是否大于2 ^ 20等...这将是log(n)检查,但我不确定比特检查与完整int比较的效率。

可以在其中获得一些性能数据。

答案 2 :(得分:1)

使用Sean Eron Anderson的优秀Bit Twiddling Hacks页面描述的de Bruijn序列有一种特别有效的算法:

uint32_t v; // find the log base 2 of 32-bit v
int r;      // result goes here

static const int MultiplyDeBruijnBitPosition[32] = 
{
  0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30,
  8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31
};

v |= v >> 1; // first round down to one less than a power of 2 
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;

r = MultiplyDeBruijnBitPosition[(uint32_t)(v * 0x07C4ACDDU) >> 27];

它可以在没有分支的13个操作中运行!

答案 3 :(得分:1)

您基本上是在尝试计算:floor(log2(x))

以对数为基数2,然后发言。

在C中执行此操作的最便携方式是使用logf()函数,该函数查找基本 e 的日志,然后调整:log2(x) == logf(x) / logf(2.0)

请在此处查看答案:How to write log base(2) in c/c++

如果您只是将生成的浮点值转换为int,则可以同时计算floor()

但是,如果您可以使用它并且可以使用它,那么计算浮点数log2()的速度非常快:logbf()

从手册页:

   The inte-
   ger constant FLT_RADIX, defined in <float.h>, indicates the radix  used
   for  the  system's  floating-point  representation.  If FLT_RADIX is 2,
   logb(x) is equal to floor(log2(x)), except that it is probably faster.

http://linux.die.net/man/3/logb

如果您考虑如何存储浮点数,您会发现值floor(log2(x))是数字的一部分,如果您只是提取该值,那么您就完成了。一点点移位和位掩码,并从指数中减去偏差(或者从技术上减去“有效数”),你就可以得到它。为任何浮点值floor(log2(x))计算x的最快方法。

http://en.wikipedia.org/wiki/Single_precision

但实际上logbf()会在将结果提供给您之前将结果转换为浮点数,并处理错误。如果你编写自己的函数来将指数提取为整数,那么它会稍快一些,无论如何都是你想要的整数。如果你想编写自己的函数,你需要使用C union来访问浮点内的位;尝试使用指针将获得与“打字”相关的警告或错误,至少在GCC上。如果你问,我会详细介绍如何做到这一点。我之前编写过这段代码,作为inline函数。

如果您只需测试一小部分数字,则可以将数字转换为整数,然后使用查找表。

答案 4 :(得分:1)

您可以使用浮动数字表示:

double n_bytes = numOfBytes

取指数位应该给出结果,因为浮点数表示为:

(-1)^S X (1. + M) X 2^E

其中: S - 签名 M - Mantissa E - 指数

要构造掩码和移位,您必须阅读有关正在使用的浮点类型的确切位模式。

CPU浮点支持为您完成大部分工作。

更好的方法是使用内置函数:

double frexp (double x, int * exp );

Floating point representation

答案 5 :(得分:0)

在具有硬件浮点单元的任何机器上通常都很快:

((union { float val; uint32_t repr; }){ x }.repr >> 23) - 0x7f

它做出的唯一假设是浮点是IEEE和整数和浮点字节序匹配,这两者在基本上所有真实世界系统(当然都是现代系统)中都是正确的。

修改:过去我使用过此功能时,我不需要大数字。 Eric指出,对于不适合浮动的整数,它会给出错误的结果。这是一个修订版(虽然可能更慢),修复了该版本并支持最多52位的值(特别是所有32位正整数输入):

((union { double val; uint64_t repr; }){ x }.repr >> 52) - 0x3ff

另请注意,我假设x是正数(不仅是非负数,也是非零数)。如果x为负数,则会得到虚假结果,如果x为0,则会得到较大的负结果(近似负对数为对数)。

答案 6 :(得分:0)

#include <Limits.h>  // For CHAR_BIT.
#include <math.h>    // For frexp.
#include <stdio.h>   // For printing results, as a demonstration.


// These routines assume 0 < x.


/*  This requires GCC (or any other compiler that supplies __builtin_clz).  It
    should perform well on any machine with a count-leading-zeroes instruction
    or something similar.
*/
static int log2A(unsigned int x)
{
    return sizeof x * CHAR_BIT - 1 - __builtin_clz(x);
}


/*  This requires that a double be able to exactly represent any unsigned int.
    (This is true for 32-bit integers and 64-bit IEEE 754 floating-point.)  It
    might perform well on some machines and poorly on others.
*/
static int log2B(unsigned int x)
{
    int exponent;
    frexp(x, &exponent);
    return exponent - 1;
}


int main(void)
{
    // Demonstrate the routines.
    for (unsigned int x = 1; x; x <<= 1)
        printf("0x%08x:  log2A -> %2d, log2B -> %2d.\n", x, log2A(x), log2B(x));

    return 0;
}