簡體   English   中英

如何獲得 C 中最右邊設置位的 position

[英]How to get position of right most set bit in C

int a = 12;

例如:12 的二進制是 1100,所以答案應該是 3,因為設置了右起第 3 位。

我想要 a 的最后一個最設置位a position。 誰能告訴我我該怎么做。

注意:我只想要 position,這里我不想設置或重置該位。 所以它不是關於 stackoverflow 的任何問題的重復。

這個答案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

查找最低有效設置位的(基於 0 的)索引等效於計算給定整數有多少個尾隨零。 根據您的編譯器,有內置函數,例如 gcc 和 clang 支持__builtin_ctz 對於 MSVC,您需要實現自己的版本,這個對不同問題的回答顯示了使用 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

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

這是有效的,因為當你計算 (n-1) .. 你實際上是讓所有的零直到最右邊的位為 1,最右邊的位為 0。然后你取一個 NOT .. 這給你留下以下內容: x= ~(來自原始數字的位)+(最右邊的 1 位)+ 尾隨零

現在,如果你這樣做 (n & x),你就會得到你所需要的,因為 n 和 x 中唯一為 1 的位是最右邊的位。

Phewww .. :sweat_smile:

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

這里可以使用 2s-complement 的性質。
找到數字的 2s 補碼的最快方法是獲取最右邊的設置位並將所有內容翻轉到它的左邊。
例如:考慮一個 4 位系統
4=0100
4 = 1100 的 2s 補碼,除了 -4
4&(-4)=0100。
請注意,只有一個設置位,其最右邊的設置位為 4
類似地,我們可以將其推廣到 n。
n&(-n) 將只包含一個設置位,它實際上位於 n 的最右邊的設置位位置。
由於 n&(-n) 中只有一個設置位,因此它是 2 的冪。
所以最后我們可以通過以下方式獲得位位置:

log2(n&(-n))+1

在 Knuth 7.1.3 中有一個巧妙的技巧,您可以乘以一個“神奇”數字(通過蠻力搜索找到),該數字將數字的前幾位映射到最右邊位的每個位置的唯一值,並且那么你可以使用一個小的查找表。 這是 32 位值的該技巧的實現,改編自nlopt 庫(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];
}

1-減去 1 個表格編號:(a-1)

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

3- 用原數進行“ AND ”運算:

int last_set_bit = a & ~(a-1)

減法背后的原因是,當您取反時,它會將最后一位設置為 1,因此當取“AND”時,它會給出最后一位。

檢查a & 1是否為 0。如果是,則右移 1 直到它不為零。 你移位的次數是從右邊有多少位是最右邊的位。

您可以通過對n(n&(n-1) )進行按位異或來找到最右邊的設置位的位置

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

我繼承了這個,並注明它來自 HAKMEM(在此處嘗試)。 它適用於有符號和無符號整數,邏輯或算術右移。 它也非常有效。

#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);
}

嘗試這個

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

解釋:
答案所述, n&(n-1)取消設置最后一個設置位。
因此,如果我們取消設置最后一個設置位並將其與數字異或; 根據異或運算的性質,最后一個設置位將變為 1,其余位將返回 0

您必須檢查從索引 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即使它是一個不同的大小

根據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;
    }

返回 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(答案)。

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

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

讓 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";
}```

好吧,所以數字系統只是使用對數和指數。 因此,我將深入研究一種對我來說真正有意義的方法。

我希望你能讀到這篇文章,因為我在那里寫了我如何將對數解釋為。

當您執行x & -x操作時,它會為您提供最右邊位為1的值(例如,它可以是00010000000010 。現在根據我如何解釋對數,這個最右邊的值集bit,是我以2的速率增長后的最終值。現在我們有興趣找到這個答案中的位數,因為無論是什么,如果從中減去1,這就是set bit的位數(這里的位計數從 0 開始,數字計數從 1 開始,所以是的)。但是數字的數量恰好是time you expanded for + 1 (按照我的邏輯)或者只是我在上一個鏈接中提到的公式. 但是現在,因為我們並不真正需要數字,而是需要位數,而且我們也不必擔心可能是真實的位的值(如果數字是 65),因為數字總是2 的一些倍數(1 除外)。所以如果你只取值x & -x的對數,我們得到位數,我確實看到了一個答案 r 之前提到過這個。 但深入探究它真正有效的原因是我想寫下來的。

PS:您也可以計算位數,然后從中減去 1 以獲得位數。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM