Wydajne, Oddział-Free Portable i Generic (ale brzydki) Realizacja
C:
#include <limits.h> /* CHAR_BIT */
#define BIT_MASK(__TYPE__, __ONE_COUNT__) \
((__TYPE__) (-((__ONE_COUNT__) != 0))) \
& (((__TYPE__) -1) >> ((sizeof(__TYPE__) * CHAR_BIT) - (__ONE_COUNT__)))
C++:
#include <climits>
template <typename R>
static constexpr R bitmask(unsigned int const onecount)
{
// return (onecount != 0)
// ? (static_cast<R>(-1) >> ((sizeof(R) * CHAR_BIT) - onecount))
// : 0;
return static_cast<R>(-(onecount != 0))
& (static_cast<R>(-1) >> ((sizeof(R) * CHAR_BIT) - onecount));
}
Usage (Producing kompilacji Stałe)
BIT_MASK(unsigned int, 4) /* = 0x0000000f */
BIT_MASK(uint64_t, 26) /* = 0x0000000003ffffffULL */
Przykład
#include <stdio.h>
int main()
{
unsigned int param;
for (param = 0; param <= 32; ++param)
{
printf("%u => 0x%08x\n", param, BIT_MASK(unsigned int, param));
}
return 0;
}
Wyjście
0 => 0x00000000
1 => 0x00000001
2 => 0x00000003
3 => 0x00000007
4 => 0x0000000f
5 => 0x0000001f
6 => 0x0000003f
7 => 0x0000007f
8 => 0x000000ff
9 => 0x000001ff
10 => 0x000003ff
11 => 0x000007ff
12 => 0x00000fff
13 => 0x00001fff
14 => 0x00003fff
15 => 0x00007fff
16 => 0x0000ffff
17 => 0x0001ffff
18 => 0x0003ffff
19 => 0x0007ffff
20 => 0x000fffff
21 => 0x001fffff
22 => 0x003fffff
23 => 0x007fffff
24 => 0x00ffffff
25 => 0x01ffffff
26 => 0x03ffffff
27 => 0x07ffffff
28 => 0x0fffffff
29 => 0x1fffffff
30 => 0x3fffffff
31 => 0x7fffffff
32 => 0xffffffff
Wyjaśnienie
Przede wszystkim, jak już omówione w innych odpowiedzi, >>
jest używany zamiast <<
w celu uniknięcia problemu, gdy liczba przesunięcie jest równe do liczby bitów typu przechowywania wartości. (Dzięki Julien's answer above dla idei)
Dla ułatwienia dyskusji, niech „instancji” makro z unsigned int
jak __TYPE__
i zobaczyć, co się dzieje (przy założeniu 32-bit na razie):
((unsigned int) (-((__ONE_COUNT__) != 0))) \
& (((unsigned int) -1) >> ((sizeof(unsigned int) * CHAR_BIT) - (__ONE_COUNT__)))
Skupmy na:
((sizeof(unsigned int) * CHAR_BIT)
pierwsza. sizeof(unsigned int)
jest znany podczas kompilacji. Jest równa 4
zgodnie z naszym założeniem. CHAR_BIT
reprezentuje liczbę bitów na char
, a.k.a. na bajt. Jest również znany podczas kompilacji. Jest równy 8
na większości maszyn na Ziemi. Ponieważ to wyrażenie jest znane podczas kompilacji, kompilator prawdopodobnie wykonałby mnożenie w czasie kompilacji i potraktowałby go jako stałą, która w tym przypadku równa się 32
. ruch
Przejdźmy do:
((unsigned int) -1)
Jest równa 0xFFFFFFFF
. Przesyłanie -1
do dowolnego typu bez znaku powoduje utworzenie wartości "all-1s" w tym typie. Ta część jest także stałą czasową kompilacji.
Do tej pory, wyrażenie:
(((unsigned int) -1) >> ((sizeof(unsigned int) * CHAR_BIT) - (__ONE_COUNT__)))
jest w rzeczywistości taki sam jak:
0xffffffffUL >> (32 - param)
która jest taka sama jak odpowiedź Juliena powyżej. Jednym z problemów z jego odpowiedzią jest to, że jeśli param
jest równe 0
, tworząc wyrażenie 0xffffffffUL >> 32
, wynikiem wyrażenia będzie 0xffffffffUL
, zamiast oczekiwanego 0
!(Dlatego właśnie nazwać moją parametr jako __ONE_COUNT__
podkreślić swój zamiar)
Aby rozwiązać ten problem, możemy po prostu dodać szczególny przypadek dla __ONE_COUNT
równa 0
użyciu if-else
lub ?:
, tak:
#define BIT_MASK(__TYPE__, __ONE_COUNT__) \
(((__ONE_COUNT__) != 0) \
? (((__TYPE__) -1) >> ((sizeof(__TYPE__) * CHAR_BIT) - (__ONE_COUNT__)))
: 0)
Ale bezgałęziowy kod jest chłodniejszy, prawda ?! Przejdźmy do następnej części:
((unsigned int) (-((__ONE_COUNT__) != 0)))
Zacznijmy od najgłębszego wyrażenia do najbardziej zewnętrznego. ((__ONE_COUNT__) != 0)
produkuje 0
, gdy parametr to 0
, lub 1
w przeciwnym razie. (-((__ONE_COUNT__) != 0))
produkuje 0
, gdy parametr to 0
, lub -1
w przeciwnym razie. W przypadku ((unsigned int) (-((__ONE_COUNT__) != 0)))
, sztuczka typu rzutowego ((unsigned int) -1)
została już wyjaśniona powyżej. Czy zauważyłeś teraz sztuczkę? Wyrażenie:
((__TYPE__) (-((__ONE_COUNT__) != 0)))
równa się "all-0s" jeśli __ONE_COUNT__
wynosi zero, a "all-1s" inaczej. Działa jako maska bitowa dla wartości obliczonej w pierwszym kroku. Tak więc, jeśli __ONE_COUNT__
jest niezerowa, maska jako brak efektu i jest taka sama jak odpowiedź Juliena. Jeśli __ONE_COUNT__
jest 0
, zamaskowuje wszystkie fragmenty odpowiedzi Juliena, tworząc stałe zero. Wizualizację, oglądać to:
__ONE_COUNT__ : 0 Other
------------- --------------
(__ONE_COUNT__) 0 = 0x000...0 (itself)
((__ONE_COUNT__) != 0) 0 = 0x000...0 1 = 0x000...1
((__TYPE__) (-((__ONE_COUNT__) != 0))) 0 = 0x000...0 -1 = 0xFFF...F
Przechodzenie przez opisie, będzie to prawdopodobnie najprostszy można zrobić .. w oczekiwaniu na dowolny wbudowany rzeczy: P – glasnt