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
Author: Robert Gamble, 2008-11-25

5 answers

~(~0

 41
Author: Darius Bacon,
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;
 29
Author: Jonathan Leffler,
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;
}
 5
Author: quinmars,
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.

 0
Author: Nubcake,
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.

 0
Author: wim,
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