[英]UNDERSTANDING how to count trailing zeros for a number using bitwise operators in C
注意- 這不是這個問題的重復 - 並行計算右側的連續零位(尾隨):解釋? . 鏈接的問題有不同的上下文,它只詢問使用signed()
的目的。 不要將此問題標記為重復。
我一直在尋找一種方法來獲取數字中尾隨零的數量。 我發現斯坦福大學有點亂寫在這里,給出了以下解釋。
unsigned int v; // 32-bit word input to count zero bits on right
unsigned int c = 32; // c will be the number of zero bits on the right
v &= -signed(v);
if (v) c--;
if (v & 0x0000FFFF) c -= 16;
if (v & 0x00FF00FF) c -= 8;
if (v & 0x0F0F0F0F) c -= 4;
if (v & 0x33333333) c -= 2;
if (v & 0x55555555) c -= 1;
為什么這最終會起作用? 我了解十六進制數如何表示為二進制和按位運算符,但我無法弄清楚這個工作背后的直覺? 工作機制是什么?
代碼已損壞(存在未定義的行為)。 這是一個固定版本,它也更容易理解(並且可能更快):
uint32_t v; // 32-bit word input to count zero bits on right
unsigned c; // c will be the number of zero bits on the right
if (v) {
v &= -v; // keep rightmost set bit (the one that determines the answer) clear all others
c = 0;
if (v & 0xAAAAAAAAu) c |= 1; // binary 10..1010
if (v & 0xCCCCCCCCu) c |= 2; // binary 1100..11001100
if (v & 0xF0F0F0F0u) c |= 4;
if (v & 0xFF00FF00u) c |= 8;
if (v & 0xFFFF0000u) c |= 16;
}
else c = 32;
一旦我們知道只有一位被設置,我們一次確定結果的一位,通過同時測試結果為奇數的所有位,然后結果設置為 2 的所有位,等等。
原始代碼反向工作,從結果集的所有位開始(在if (c) c--;
),然后確定哪些需要為零並清除它們。
由於我們一次只學習一點輸出,我認為使用位操作而不是算術來構建輸出更清晰。
這段代碼(來自網絡)主要是 C 語言,盡管v &= -signed(v);
不正確 C. 目的是讓它表現為v &= ~v + 1;
首先,如果v
為零,那么在&
操作之后它仍然為零,並且所有的if
語句都被跳過,所以你得到 32。
否則, &
操作(修正后)會清除最右邊 1 左邊的所有位,因此此時v
包含單個 1 位。 然后c
遞減到31,即可能結果范圍內的所有1 位。
然后if
語句一次一位確定其數字位置(位置編號的一位,而不是v
一位),清除應為 0 的位。
代碼首先轉換 v 的方式是完全為空的,除了最左邊的一個。 然后,它確定第一個的位置。
首先讓我們看看我們如何抑制除最左邊的之外的所有。
假設k是v中最左邊的位置。v=(vn-1,vn-2,..vk+1,1,0,..0)。
-v 是添加到 v 將給出 0 的數字(實際上它給出 2^n,但如果我們只保留 n 個不太重要的位,則忽略位 2^n)。
-v 中位的值必須是多少才能使 v+-v=0?
顯然 -k 的 k-1..0 位必須為 0,以便添加到 v 中的尾隨零,它們給出零。
位 k 必須為 1。添加到 vk 中的 1,它將以 k+1 的順序給出 0 和 1 的進位
-v 的第 k+1 位將被添加到 vk+1 和第 k 步生成的進位。 它必須是 vk+1 的邏輯補碼。 因此,無論 vk+1 的值是多少,如果 vk+1=0(或如果 vk+1=1 則為 1+1+0),我們將有 1+0+1 並且結果將在 k+1 階為 0 並帶有進位在 k+2 階生成。
這與 n-1..k+2 位類似,它們都必須是 v 中相應位的邏輯補碼。
因此,我們得到眾所周知的結果,要得到 -v,必須
保持 v 的所有尾隨零不變
保持不變 v 中最左邊的一個
補充所有其他位。
如果我們計算 v&-v,我們有
v vn-1 vn-2 ... vk+1 1 0 0 ... 0
-v & ~vn-1 ~vn-2 ... ~vk+1 1 0 0 ... 0
v&-v 0 0 ... 0 1 0 0 ... 0
所以 v&-v 只保留 v 中最左邊的一個。
要找到第一個的位置,請查看代碼:
if (v) c--; // no 1 in result? -> 32 trailing zeros.
// Otherwise it will be in range c..0=31..0
if (v & 0x0000FFFF) c -= 16; // If there is a one in left most part of v the range
// of possible values for the location of this one
// will be 15..0.
// Otherwise, range must 31..16
// remaining range is c..c-15
if (v & 0x00FF00FF) c -= 8; // if there is one in either byte 0 (c=15) or byte 2 (c=31),
// the one is in the lower part of range.
// So we must substract 8 to boundaries of range.
// Other wise, the one is in the upper part.
// Possible range of positions of v is now c..c-7
if (v & 0x0F0F0F0F) c -= 4; // do the same for the other bits.
if (v & 0x33333333) c -= 2;
if (v & 0x55555555) c -= 1;
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.