【问题标题】:How to get position of right most set bit in C如何在C中获得最右边的设置位的位置
【发布时间】:2015-10-02 07:00:18
【问题描述】:
int a = 12;

例如:12 的二进制是 1100,所以答案应该是 3,因为设置了右数第三位。

我想要a 的最后一个最多设置位的位置。谁能告诉我该怎么做。

注意:我只想要位置,这里我不想设置或重置位。所以它不是stackoverflow上任何问题的重复。

【问题讨论】:

  • @rost0031 这不是同一个问题。 ramsingh 请展示你到目前为止所做的尝试
  • 我想要准确的位置。我不想设置或重置它。
  • 我希望一步完成。
  • @RyanHaining 我在纸上试过了,你能告诉我如何在这里展示它。

标签: c bitwise-operators


【解决方案1】:

让 x 成为您的整数输入。 按位与 1。 如果它是偶数,即 0,0&1 将返回 0。 如果它是奇数,即 1, 1&1 返回 1。

if ( (x & 1) == 0) ) 
{
        std::cout << "The rightmost bit is 0 ie even \n";
}
else
{
        std::cout<< "The rightmost bit is 1 ie odd \n";
}```

【讨论】:

  • 这个问题有十五个现有答案,包括一个获得 30 票赞成的答案。您确定尚未提供您的答案吗?如果不是,为什么有人会更喜欢您的方法而不是提议的现有方法?您是否正在利用新功能?是否存在更适合您的方法的场景?
【解决方案2】:

试试这个

int set_bit = n ^ (n&(n-1));

说明:
this 回答中所述,n&amp;(n-1) 取消设置最后一个设置位。
因此,如果我们取消设置最后一个设置位并将其与数字异或;根据 xor 操作的性质,最后设置的位将变为 1,其余位将返回 0

【讨论】:

  • 为什么会产生正确的结果?稍微解释一下会很有帮助。
  • @harold 你没有正确理解它。这个问题很清楚。
  • @ramsingh,您要求的是位置而不是位的值。
  • 对于a = 8,答案应该是什么?
  • 这会返回一个整数,它是 2 的幂:它只设置了 n 中最右边的设置位。我想它被否决了,因为 OP 要求提供 log2(加一个)。即,对于 1100b,它将返回 100b (4),而不是 log2(4) + 1 = 3。我没有对此投反对票,我认为它仍然有用。
【解决方案3】:

1- 减去 1 表格编号:(a-1)

2- 取它的否定:~(a-1)

3- 对原数进行'AND'运算:

int last_set_bit = a & ~(a-1)

减法背后的原因是,当你取负时,它会将最后一位设置为 1,所以当取 'AND' 时,它会给出最后设置的位。

【讨论】:

    【解决方案4】:

    在 Knuth 7.1.3 中有一个巧妙的技巧,您可以乘以一个“神奇”数字(通过蛮力搜索找到),该数字的前几位映射到最右边每个位置的唯一值位,然后你可以使用一个小的查找表。这是 32 位值的该技巧的实现,改编自 nlopt library(MIT/expat 许可)。

    /* Return position (0, 1, ...) of rightmost (least-significant) one bit in n.
     *
     * This code uses a 32-bit version of algorithm to find the rightmost
     * one bit in Knuth, _The Art of Computer Programming_, volume 4A
     * (draft fascicle), section 7.1.3, "Bitwise tricks and
     * techniques." 
     *
     * Assumes n has a 1 bit, i.e. n != 0
     *
     */
    static unsigned rightone32(uint32_t n)
    {
        const uint32_t a = 0x05f66a47;      /* magic number, found by brute force */
        static const unsigned decode[32] = { 0, 1, 2, 26, 23, 3, 15, 27, 24, 21, 19, 4, 12, 16, 28, 6, 31, 25, 22, 14, 20, 18, 11, 5, 30, 13, 17, 10, 29, 9, 8, 7 };
        n = a * (n & (-n));
        return decode[n >> 27];
    }
    

    【讨论】:

      【解决方案5】:
      int main(int argc, char **argv)
      {
          int setbit;
          unsigned long d;
          unsigned long n1;
          unsigned long n = 0xFFF7;
          double nlog2 = log(2);
      
          while(n)
          {
              n1 = (unsigned long)n & (unsigned long)(n -1);
              d = n - n1;
              n = n1;
      
              setbit = log(d) / nlog2;
              printf("Set bit: %d\n", setbit);
          }
      
          return 0;
      }
      

      结果如下。

      Set bit: 0
      Set bit: 1
      Set bit: 2
      Set bit: 4
      Set bit: 5
      Set bit: 6
      Set bit: 7
      Set bit: 8
      Set bit: 9
      Set bit: 10
      Set bit: 11
      Set bit: 12
      Set bit: 13
      Set bit: 14
      Set bit: 15
      

      【讨论】:

        【解决方案6】:

        n 的最左边位可以使用以下公式获得: n & ~(n-1)

        这是有效的,因为当您计算 (n-1) .. 时,您实际上将所有零直到最右边的位设为 1,最右边的位设为 0。 然后你拿它一个NOT ..它给你留下以下内容: x= ~(原始数字的位) + (最右边的 1 位) + 尾随零

        现在,如果你这样做 (n & x),你会得到你需要的,因为在 n 和 x 中唯一为 1 的位是最右边的位。

        Phewwwww .. :sweat_smile:

        http://www.catonmat.net/blog/low-level-bit-hacks-you-absolutely-must-know/ 帮助我理解了这一点。

        【讨论】:

        • 我想你的意思是“n的最右边位可以使用公式获得:n & ~(n-1)”?除此之外,这个答案非常适合解释为什么不只是如何。
        • 与其他答案一样,这是一个很好的解释,但答案不正确。此代码返回一个仅设置了最右边的位的整数,但它不执行问题所要求的操作并返回该位的位位置(每个 OP +1)。
        【解决方案7】:

        我继承了这个,并注明它来自 HAKMEM(试试看here)。它适用于有符号和无符号整数、逻辑或算术右移。效率也很高。

        #include <stdio.h>
        
        int rightmost1(int n) {
            int pos, temp;
            for (pos = 0, temp = ~n & (n - 1); temp > 0; temp >>= 1, ++pos);
            return pos;
        }
        
        int main()
        {
            int pos = rightmost1(16);
            printf("%d", pos);
        }
        

        【讨论】:

          【解决方案8】:

          x & ~(x-1) 隔离最低位为 1。

          【讨论】:

            【解决方案9】:

            这个答案Unset the rightmost set bit 告诉了如何获取和取消设置最右边的设置位对于表示为二进制补码的无符号整数或有符号整数

            获取最右边的设置位

            x & -x
            // or
            x & (~x + 1)
            

            取消最右边的设置位

            x &= x - 1
            // or
            x -= x & -x  // rhs is rightmost set bit
            

            为什么会起作用

            x:                     leading bits  1  all 0
            ~x:           reversed leading bits  0  all 1
            ~x + 1 or -x: reversed leading bits  1  all 0
            x & -x:                       all 0  1  all 0
            

            例如,设x = 112,为简单起见选择8位,尽管对于所有整数大小的想法都是相同的。

            // example for get rightmost set bit
            x:             01110000
            ~x:            10001111
            -x or ~x + 1:  10010000
            x & -x:        00010000
            
            // example for unset rightmost set bit
            x:             01110000
            x-1:           01101111
            x & (x-1):     01100000
            

            【讨论】:

            • 这是一个很好的解释,但却是一个错误的答案。此代码返回一个仅设置了最右边的位的整数,但它不执行问题所要求的操作并返回该位的位位置(每个 OP +1)。
            【解决方案10】:

            返回log2(((num-1)^num)+1);

            举例说明:12 - 1100

            num-1 = 11 = 1011

            num^ (num-1) = 12^11 = 7 (111)

            num^ (num-1))+1 = 8 (1000)

            log2(1000) = 3(答案)。

            【讨论】:

              【解决方案11】:

              你可以通过 n(n&(n-1) )

              的按位异或找到最右边的设置位的位置
              int pos = n ^ (n&(n-1));
              

              【讨论】:

              • 你确定这段代码给出了最右边的设置位的位置吗?
              • 这是错误的。如果 n=4 或 0b100,答案应该是 2,假设 0 索引。 100 & 011 = 000; 100 ^ 000 = 100,即 4 而不是预期的 2。
              【解决方案12】:

              这里可以使用 2s-complement 的性质。
              找到一个数字的 2s 补码的最快方法是获取最右边的设置位并将所有内容翻转到它的左侧。
              例如:考虑一个 4 位系统
              4=0100
              4 的 2s 补码 = 1100,不过是 -4
              4&(-4)=0100.
              请注意,只有一个设置位,其最右边的设置位为 4
              同样,我们可以将其推广到 n。
              n&(-n) 将仅包含一个设置位,该设置位实际上位于 n 的最右侧设置位位置。
              由于 n&(-n) 中只有一个设置位,因此它是 2 的幂。
              所以最后我们可以通过以下方式获得位位置:

              log2(n&(-n))+1

              【讨论】:

              • 此解决方案是否应明确处理 (n & -n) == 0 时的情况?因为 log2(0) 产生的 -inf 不是一个有效的整数?
              • 只有当 n = 0 时才会发生这种情况,您可能需要明确检查。
              【解决方案13】:

              找到最低有效位的(从 0 开始的)索引相当于计算给定整数有多少个尾随零。根据您的编译器,有为此的内置函数,例如 gcc 和 clang 支持 __builtin_ctz。 对于 MSVC,您需要实现自己的版本,this answer 到另一个问题显示了使用 MSVC 内在函数的解决方案。

              鉴于您正在寻找基于 1 的索引,您只需将 1 添加到 ctz 的结果中即可实现您想要的。

              int a = 12;
              int least_bit = __builtin_ctz(a) + 1; // least_bit = 3
              

              请注意,如果a == 0,此操作未定义。此外,如果您使用的是longlong long 而不是int,则应该使用__builtin_ctzl__builtin_ctzll

              【讨论】:

              • 嗨,似乎是最好的解决方案(!)。补充解释的建议......关于“最右最”,“最左”,“从最重要开始位位置”和“... 最不重要 ...”。对 CLZ 和 CTZ 以及它的__builtin_cXz 感到如此困惑……你能解释一下行话和 CLZ/CTZ 的选择吗?
              • 我不同意最右边和最左边在这里混淆。任何在纸上数到十的人都知道,右边的数字不如左边的数字重要。
              • 我认为这是最好的答案。它解决了正确的问题,并且在恒定时间内完成,并且不需要日志功能。
              【解决方案14】:

              根据dbush的解决方案,试试这个:

              int rightMostSet(int a){
                    if (!a) return -1;  //means there isn't any 1-bit
                    int i=0;
                    while(a&1==0){ 
                      i++; 
                      a>>1;
                    }
                    return i;
                  }
              

              【讨论】:

              • 在进入循环之前检查数字是否为零(即所有位都为零)会更容易。
              【解决方案15】:

              您必须检查从索引 0 开始的所有 32 位,并一直向左移动。如果您可以按位-并且您的a 在该位置有一个位并返回一个非零值,则表示该位已设置。

              #include <limits.h>
              
              int last_set_pos(int a) {
                for (int i = 0; i < sizeof a * CHAR_BIT; ++i) {
                  if (a & (0x1 << i)) return i;
                }
                return -1; // a == 0
              }
              

              在典型的系统上,int 是 32 位,但是 sizeof a * CHAR_BIT 会在 a 中得到正确的位数,即使它的大小不同

              【讨论】:

                【解决方案16】:

                检查a &amp; 1 是否为 0。如果是,则右移 1 直到它不为零。您移位的次数是设置的最右边的位从右边开始的多少位。

                【讨论】:

                  猜你喜欢
                  • 2021-04-25
                  • 2021-01-08
                  • 2011-06-09
                  • 1970-01-01
                  • 1970-01-01
                  • 2016-02-05
                  • 1970-01-01
                  • 2019-03-31
                  • 2011-04-22
                  相关资源
                  最近更新 更多