【问题标题】:Find out how many binary digits a particular integer has [duplicate]找出一个特定整数有多少个二进制数字[重复]
【发布时间】:2012-01-26 00:17:12
【问题描述】:

可能重复:
Compute fast log base 2 ceiling

在 C/C++ 中将特定整数从十进制转换为二进制时,找出特定整数有多少个二进制数字的最快方法是什么?

例如。 47(10) = 101111(2)

所以 47 有 6 位二进制表示。

【问题讨论】:

标签: c++ c algorithm


【解决方案1】:

要获得一种无需调用数学函数的快速有趣的方法,请查看以下内容:

for (digits = 0; val > 0; val >>= 1)
        digits++;

作为奖励,这应该归结为内存负载和正在使用的 2 个寄存器,以获得额外的惊喜。

【讨论】:

  • 我认为这是迄今为止最好的答案..
  • 漂亮而简单。具有线性复杂性。
  • 太简单了。 OP 说“最快的方法”,而不是“最简单的”(这真的比使用内部函数更简单吗?)
  • 这不是“最快的方法”,因此 OP 的问题与他标记为接受的答案之间存在分歧。也许这是一个语言障碍或“最快的方式”意味着最快的打字而不是最快的执行?...
  • OP 对“最快”的含义并不太具体。考虑到正确的数据集、计算机架构和/或编译时优化,这很容易成为最快的解决方案。这绝对是我理解最快的。
【解决方案2】:

如果您正在寻找性能方面“最快”的方法,则需要使用特定于平台的方法。

有些架构实际上有这样的指令。

在 x86 上,您有 bsr 指令。

在 MSVC 中,它可以通过以下方式访问:

inline int bitlength(unsigned long x){
    if (x == 0)
        return 0;

    unsigned long index;
    _BitScanReverse(&index,x);
    return (int)(index + 1);
}

GCC__builtin_clz() intrinsic - 做类似的事情。

【讨论】:

  • 这是应该被标记为正确的答案(也是在我之前一分钟出现的)。 BSR 操作码是 x86 最快的方法,在一般情况下,分支 IF 语句可能最快。
  • 为了完整起见,我在帖子中提到的分支方法 IF 也比 ORing 方法(在另一个答案中)快,但比 BSR 指令慢。 snipt.org/xrom3 (312ms BSR, 406ms IFs, 671ms OR) 我使用 FlatAssembler 对函数进行基准测试。
【解决方案3】:

my favorite collection of bit twiddling hacks 提供的最快解决方案是Find the log base 2 of an N-bit integer in O(lg(N)) operations with multiply and lookup。它需要 13 条指令才能找到一个数字中的最高设置位。

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];

【讨论】:

  • 虽然我认为这种 ORing 方法很酷(作为一名程序员,我可以从艺术上欣赏它),但它并不是最优的。你做了 10 次 OR 和 SHIFT 操作,然后你用另一个 SHIFT 做一个 MUL,然后你做一个内存查找 (LUT)。可以说这与提出的 Log2 方法一样慢。
  • @LastCoder 你设想 log2 在幕后做什么?这个实现公开承认它需要 13 条(非 FP)指令。
  • 如果目标架构有快速缓存,那么 LUT 应该不会太贵。
  • 刚刚在我的基于 Intel i7 的 MacBook Pro 上测量。 ceil(log2(n)) 的 1,000,000 次迭代耗时 33,132 微秒,而 bit-twiddling 版本的 1,000,000 次迭代耗时 8,410 微秒——大约快了 75%。
  • x87-FPU指令FYL2XP1怎么样。这是我期望 Log2 在所有 x86 和 x86-64 位机器上使用的。它将数字放入 ST(0) 并将常量 1 放入 ST(1),然后执行 FYL2XP1,执行 ST0 = ST1 * log2( ST0 + 1.0 ),然后您将弹出/存储 ST(0 ) 再次作为整数。因此,最佳 FPU 版本总共需要 4 条指令。
【解决方案4】:

传统方式

int count = 32;
for(int i = 1 << 31; i != 0; i >>= 1, count--)
    if((number & i) != 0) return count;

您可以通过优化获得更多幻想。

EDIT 2 这是不使用位扫描反向操作码时我能想到的最快的代码。您可以使用更大的(256 个条目)LUT 并删除最后一个 IF 语句。在我的测试中,这比另一篇文章中描述的重复 OR-SHIFT 然后 LUT 方法要快。

int[] Log2_LUT = new int[16]{0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4};
int Log2 (number) {
int count = 0;
if((number & 0xFFFF0000) != 0) { 
    number >>= 16;
    count += 16;
}
if((number & 0x0000FF00) != 0) { 
    number >>= 8;
    count += 8
}
if((number & 0x000000F0) != 0) {
    number >>= 4;
    count += 4;
}
return count + Log2_LUT[number];
}

或者,如果您在 x86 或 x86-64 位架构中,您可以使用 BSR(位扫描反向)操作码。 你可以找到它的 c++ 内在函数http://msdn.microsoft.com/en-us/library/fbxyd7zd%28v=vs.80%29.aspx

你的问题也和这个What is the fastest way to calculate the number of bits needed to store a number类似

编辑为什么 log2 的答案不是最佳的... 虽然在数学上是正确的,但复杂的浮点运算(正弦、余弦、正切、对数)是现代计算机上执行速度最慢的运算。由于必须将整数转换为浮点数并且还必须对其进行上限/下限,从而加剧了这种情况。

【讨论】:

    【解决方案5】:

    尝试以 2 为底的对数:

    ceil(log2(n))
    

    【讨论】:

    • 1 怎么样?零二进制数字?
    • OP 要求尽可能快的方法,使用像log2()ceil() 这样的浮点运算肯定不是解决这类问题的最快方法.
    【解决方案6】:

    如果整数至少为1,则所需的位为:

    floor(log2(x)) + 1
    

    【讨论】:

    • OP 要求尽可能快的方法,而使用像log2()ceil() 这样的浮点运算肯定不是解决这类问题的最快方法.
    • 同意。我没看到。投票结束作为欺骗。
    【解决方案7】:

    如果速度比可移植性更重要,那么一些编译器会提供“计数前导零”功能。这在包括现代 x86 和 ARM 在内的某些处理器上编译为单个机器指令。例如,使用 GCC:

    CHAR_BIT * sizeof x - __builtin_clz(x)
    

    【讨论】:

      【解决方案8】:

      尝试使用对数:

      ceil(log2(x))
      

      【讨论】:

      • floor 是错误的 - 使用问题中的示例,log2(47) = 5.55;取其中的floor 给出 5 而不是正确的 6。
      • 要在数学上挑剔它是floor(log2(x)) + 1
      • OP 要求尽可能快的方式,使用像log2()ceil() 这样的浮点运算肯定不是解决这类问题的最快方法.
      • ceil(log2(x)) 不正确。应该是floor(log2(x))+1
      【解决方案9】:

      一种方法是这样...

      unsigned int count_bits(unsigned int n)
      {
          unsigned int count = 0;
      
          if ( n <= 1 )
            return 1;
      
          do
            count++;
          while( n >>= 1 );
      
          return count;
      }
      

      【讨论】:

        猜你喜欢
        • 2020-03-31
        • 1970-01-01
        • 1970-01-01
        • 2020-08-27
        • 1970-01-01
        • 2018-05-18
        • 1970-01-01
        • 2021-05-12
        • 2014-03-04
        相关资源
        最近更新 更多