Maska bitowa w C
Jaki jest najlepszy sposób na zbudowanie maski bitowej w C z m
ustawionymi bitami poprzedzonymi k
unset bitami, a następnie n
unset bitami:
00..0 11..1 00..0
k m n
Na przykład, k= 1, m = 4, n=3 spowoduje maskę bitową:
01111000
5 answers
~(~0
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-25 06:21:44
Więc prosisz o bity zestawu m poprzedzone bitami resetu k, a następnie N bitami resetu? Możemy zignorować k, ponieważ będzie on w dużej mierze Ograniczony wyborem typu integer.
mask = ((1 << m) - 1) << n;
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-25 06:21:47
Lubię oba rozwiązania. Oto inny sposób, który przychodzi mi do głowy (chyba nie lepszy).
((~((unsigned int)0) << k) >> (k + n)) << n
Edytuj:
Był błąd w mojej poprzedniej wersji(był bez unsigned int cast). Problem polegał na tym, że ~0 >> n
dodaje 1s z przodu, a nie 0s.
I tak to podejście ma jeden duży minus; zakłada, że znasz liczbę bitów domyślnego typu integer lub innymi słowy zakłada, że naprawdę znasz k, podczas gdy inne rozwiązania są niezależne od k. To sprawia, że moja wersja jest mniej przenośna, a przynajmniej trudniejsza do przeniesienia. (Wykorzystuje również 3 przesunięcia oraz operator dodawania i bitowej negacji, czyli dwie dodatkowe operacje.)
Więc lepiej byłoby użyć jednego z innych przykładów.
Oto mała aplikacja testowa, wykonana przez Jonathana Lefflera, aby porównać i zweryfikować wyniki różnych rozwiązań:]}#include <stdio.h>
#include <limits.h>
enum { ULONG_BITS = (sizeof(unsigned long) * CHAR_BIT) };
static unsigned long set_mask_1(int k, int m, int n)
{
return ~(~0 << m) << n;
}
static unsigned long set_mask_2(int k, int m, int n)
{
return ((1 << m) - 1) << n;
}
static unsigned long set_mask_3(int k, int m, int n)
{
return ((~((unsigned long)0) << k) >> (k + n)) << n;
}
static int test_cases[][2] =
{
{ 1, 0 },
{ 1, 1 },
{ 1, 2 },
{ 1, 3 },
{ 2, 1 },
{ 2, 2 },
{ 2, 3 },
{ 3, 4 },
{ 3, 5 },
};
int main(void)
{
size_t i;
for (i = 0; i < 9; i++)
{
int m = test_cases[i][0];
int n = test_cases[i][1];
int k = ULONG_BITS - (m + n);
printf("%d/%d/%d = 0x%08lX = 0x%08lX = 0x%08lX\n", k, m, n,
set_mask_1(k, m, n),
set_mask_2(k, m, n),
set_mask_3(k, m, n));
}
return 0;
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-24 04:49:29
Podczas gdy najlepsze odpowiedzi są proste i skuteczne, nie ustawiają MSB dla Przypadku, Gdy n=0
i m=31
:
~(~0 << 31) << 0
= 0111 1111 1111 1111 1111 1111 1111 1111
((1 << 31)-1) << 0
= 0111 1111 1111 1111 1111 1111 1111 1111
Moja propozycja na 32-bitowe niepodpisane słowo (które jest brzydkie i ma gałąź) wygląda tak:
unsigned int create_mask(unsigned int n,unsigned int m) {
// 0 <= start_bit, end_bit <= 31
return (m - n == 31 ? 0xFFFFFFFF : ((1 << (m-n)+1)-1) << n);
}
To rzeczywiście pobiera bity z zakresu [m,n]
(zamknięty interwał), więc create_mask(0,0)
zwróci maskę dla pierwszego bitu (bit 0), a create_mask(4,6)
zwróci maskę dla bitów od 4 do 6, czyli ... 00111 0000
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-08 16:56:59
Dla tych, którzy są zainteresowani nieco bardziej wydajnym rozwiązaniem na systemy x86 z obsługą BMI2 (Intel Haswell lub nowszy, AMD Excavator lub nowszy): {]}
mask = _bzhi_u32(-1,m)<<n;
Instrukcja bzhi
zeruje wysokie bity rozpoczynające się od określonej pozycji bitowej.
_bzhi_u32
kompiluje się do tej instrukcji. Kod badania:
#include <stdio.h>
#include <x86intrin.h>
/* gcc -O3 -Wall -m64 -march=haswell bitmsk_mn.c */
unsigned int bitmsk(unsigned int m, unsigned int n)
{
return _bzhi_u32(-1,m)<<n;
}
int main() {
int k = bitmsk(7,13);
printf("k= %08X\n",k);
return 0;
}
Wyjście:
$./a.out
k= 000FE000
Fragment kodu _bzhi_u32(-1,m)<<n
kompiluje się do trzech instrukcji
movl $-1, %edx
bzhi %edi, %edx, %edi
shlx %esi, %edi, %eax
Czyli o jedną instrukcję mniej niż kody przez @Jonathan Leffler
i @Darius Bacon.
W procesorach Intel Haswell lub nowszych, zarówno bzhi
, jak i shlx
mają opóźnienie 1 cyklu i
przepustowość 2 na cykl. W przypadku AMD Ryzen te dwie instrukcje mają nawet przepustowość 4 na cykl.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-09 12:36:01