2012-09-14 55 views
12

有人可以向我解釋這個函數嗎?如何在C中創建最小有效位設置爲1的掩碼

設置爲1

實施例的至少顯著n位的掩模:

N = 6 - >值爲0x2F,N = 17 - > 0x1FFFF //我沒有得到這些在所有,特別是如何n = 6 - > 0x2F

此外,什麼是面具?

+3

*也什麼是面具* [如何維基百科?](http://en.wikipedia.org/wiki/Mask_? (計算)) – chris

+1

0x2F是錯誤的,它應該是0x3f – wich

+1

@chris wiki太混亂了...... – sebi

回答

22

通常的做法是取一個1,並將其左移n位。這會給你類似於:00100000。然後從中減去一個,這將清除已設置的位,並設置所有不重要的位,因此在這種情況下,我們會得到:00011111

掩碼通常用於按位操作,特別是and。您可以使用上面的掩碼自行獲取5個最不重要的位,與其他可能存在的其他位相隔離。在處理硬件時,這種情況尤其常見,硬件通常會有一個硬件寄存器,其中的硬件寄存器中的位代表許多完全獨立的,不相關的數量和/或標誌。

+2

請記住,去'1 << w-1',其中'w'是寬度數據類型,除了一個位外,都是UB。 – chris

+0

沒錯。歸咎於英特爾,但它達到了標準。 – wildplasser

+0

有關以這種方法從UB中統一恢復的方法,請參閱下面的答案。 – user13972

0

我相信你的第一個例子應該是0x3f

0x3f爲這是在二進制11111163十六進制表示,以使得最後的6位(最低顯著6個比特)被設置爲1

以下小C程序將計算出正確的掩模:

#include <stdarg.h> 
#include <stdio.h> 

int mask_for_n_bits(int n) 
{ 
    int mask = 0; 

    for (int i = 0; i < n; ++i) 
     mask |= 1 << i; 

    return mask; 
} 

int main (int argc, char const *argv[]) 
{ 
    printf("6: 0x%x\n17: 0x%x\n", mask_for_n_bits(6), mask_for_n_bits(17)); 
    return 0; 
} 
0

0x2F是二進制0010 1111 - 這應該是0x3f,這在二進制0011 1111並具有設定的6個最低-顯著位。

類似地,0x1FFFF是二進制的0001 1111 1111 1111 1111,其具有設置的17個最低有效位。

「掩模」是旨在與另一值進行組合使用按位操作者像&|^個別地設定,取消設置,在該另一值翻轉或離開不變的位的值。

例如,如果您使用的&運營商一定的價值n結合面具0x2F,結果將在所有零,但至少6位顯著,和那些6位將從值n複製不變。

對於&掩碼,掩碼中的二進制0表示「無條件地將結果位設置爲0」,而1表示「將結果位設置爲輸入值位」。對於|掩模,在掩模中的0設置結果位到輸入比特和1無條件地將結果位1,以及用於^掩模,一個0設置結果位到輸入比特和1設置結果位到輸入位的補碼。

+0

Ops。更新後得到錯誤的編輯,但我做了回滾。抱歉! – jweyrich

5

掩碼是一個整數值的通用術語,它與另一個整數值進行按位,與或與異或等操作。

例如,如果要提取int變量的8個最低有效位數,請執行variable & 0xFF。 0xFF是一個掩碼。

同樣,如果你想設置位0和8,你做variable | 0x101,其中0x101是一個掩碼。

或者如果你想反轉相同的位,你可以做variable^0x101,其中0x101是一個掩碼。

要爲您的案例生成一個掩碼,您應該利用簡單的數學事實,如果您將1添加到掩碼(掩碼將其所有最低有效位設置爲1,其餘爲0),則會得到一個值是2的冪。因此,如果你生成2的最接近的冪,那麼你可以從它減去1來獲得掩碼。

的2正極權力在C.

與左移 <<操作者容易產生

因此,1 << n產量2 Ñ。在二進制中它是10 ... 0與n 0s。

(1 << n) - 1會產生設置爲1

現在n最低位的面具,你需要提防左移溢出。在C(和C++)中,不能合法地將變量左移多個位的位置,因此如果整數是32位,則1<<32結果爲undefined behavior。也應該避免帶符號的整數溢出,所以你應該使用無符號的值,例如1u << 31

7

對於正確性和性能而言,由於在現代x86處理器(特別是BLSMSK)中出現BMI指令,因此在2012年要求回答此問題的最佳方法已經發生了變化。

下面是解決這個問題的好方法,同時保持與舊處理器的向後兼容性。

該方法是正確的,而當前的最佳答案會在邊緣情況下產生未定義的行爲。

當允許使用BMI指令進行優化時,Clang和GCC會將gen_mask()簡化爲兩個操作。隨着支持硬件,一定要添加的編譯器標誌爲BMI說明: -mbmi -mbmi2

#include <inttypes.h> 
#include <stdio.h> 

uint64_t gen_mask(const uint_fast8_t msb) { 
    const uint64_t src = (uint64_t)1 << msb; 
    return (src - 1)^src; 
} 

int main() { 
    uint_fast8_t msb; 
    for (msb = 0; msb < 64; ++msb) { 
    printf("%016" PRIx64 "\n", gen_mask(msb)); 
    } 
    return 0; 
} 
+0

你有一個錯誤的AFAICT錯誤。 –

+1

對不起,這是一個誤解:我會使用寬度作爲參數(如OP提到的N),但由於您使用MSB的索引,它實際上是一致的。 –

+0

在這種情況下,consting會做什麼? –

相關問題