(src >> start) & ((1 << len)-1)
jest sposobem wyrażania ekstrakcji len
bitów, zaczynając start
. (W tym przypadku, start
jest LSB zakresu, który chcesz. Twoja funkcja wymaga MSB jako wejście.) To wyrażenie pochodzi z Wikipedia's article on the x86 BMI1 instruction set extensions.
Oba sposoby tworzenia maski wyglądają na ryzykowne w przypadku, gdy len
to pełna szerokość tego typu. (Narożny przypadek wydobywania wszystkich bitów). Przesunięcia o pełną szerokość typu mogą albo dać zero, albo niezmienione. . (IIRC, to faktycznie wywołuje niezdefiniowanej zachowanie, ale to, co dzieje się w praktyce na przykład x86 masek przesunięcie odliczanie do zakresu 0-31 (dla przesunięcia 32bit) Z int 32bit.
Jeśli 1 < < 32 daje 1, a następnie 1/1 = 0, to wynikiem będzie równa zero.
Jeśli ~ 0 < < 32 wytwarza ~ 0, raczej niż 0, maska zero.
Myślę, że z twoich przykładów potrzebujesz bitów [iStartPos : iStartPos - iNumOfBites]
, gdzie bity są ponumerowane od zera.
Najważniejszą rzeczą, którą zmienię w Twojej funkcji, to nazewnictwo funkcji i zmiennych oraz dodanie komentarza.
bitResult
jest wejściem do funkcji; nie używaj "wyniku" w nazwie.
iStartPos
ok, ale trochę gadatliwy
iNumOfBites
Komputery mają bity i bajty. Jeśli masz do czynienia z ugryzieniem, potrzebujesz lekarza (lub dentysty).
Ponadto typem zwrotu prawdopodobnie powinien być unsigned
.
// extract bits [msb : msb-len] from input into the low bits of the result
unsigned BitExtract(unsigned input, int msb, int len)
{
return (input >> (msb-len + 1)) & ~(~0 << len);
}
Jeżeli parametr start-pozycja była LSB, zamiast MSB wyrażenie byłoby prostsze, a kod będzie mniejszy i szybszy (chyba, że po prostu ma dodatkową pracę dla rozmówcy). Z LSB jako paramem, BitExtract ma 7 instrukcji, a 9, jeśli jest to MSB (na x86-64, gcc 5.2).
Dostępne są również instrukcje maszynowe (wprowadzone przez firmę Intel Haswell i AMD Piledriver), które wykonują tę operację. Otrzymasz nieco mniejszy i nieco szybszy kod, używając go. Używa także LSB, konwencji pozycji len, a nie MSB, więc dostajesz krótszy kod z LSB jako argumentem.
Procesory Intela znają tylko wersję, która wymaga natychmiastowego załadowania bezpośrednio do rejestru, więc gdy wartości są stałymi w czasie kompilacji, nie oszczędzają one dużo w porównaniu do zwykłego przesuwania i maskowania. e.g. see this post about using it or pextr for RGB32 -> RGB16. I oczywiście nie ma znaczenia, czy parametrem jest MSB, czy LSB o pożądanym zakresie, jeśli start i len są zarówno stałymi w czasie kompilacji.
Tylko AMD wprowadza wersję bextr
że może mieć maskę sterowania jako natychmiastowego stała, ale niestety wydaje gcc 5.2 nie korzysta z natychmiastowej wersji dla kodu, który używa wewnętrzna (nawet z -march=bdver2
(tj spychacz v2 aka piledriver). (będzie generate bextr with an immediate argument on its own in some cases z -march=bdver2
.)
I tested it out on godbolt, aby zobaczyć, jaki rodzaj kodu można dostać z lub bez bextr.
#include <immintrin.h>
// Intel ICC uses different intrinsics for bextr
// extract bits [msb : msb-len] from input into the low bits of the result
unsigned BitExtract(unsigned input, int msb, int len)
{
#ifdef __BMI__ // probably also need to check for __GNUC__
return __builtin_ia32_bextr_u32(input, (len<<8) | (msb-len+1));
#else
return (input >> (msb-len + 1)) & ~(~0 << len);
#endif
}
byłoby wziąć dodatkowy dyspozycję (a) do movzx
zaimplementować a (msb-len+1)&0xff
kontrola bezpieczeństwa, aby uniknąć rozłożenia bajtu początkowego na bajt długości. Opuściłem to, ponieważ bzdurne jest żądanie wyjścia poza zakres 0-31, nie mówiąc już o zakresie 0-255. Ponieważ nie ulegnie awarii, po prostu zwróć kilka innych nonsensownych wyników, nie ma sensu.
Zresztą bext
oszczędza sporo instrukcji (jeśli BMI2 shlx
/shrx
nie jest dostępny albo! -march=native
na godbolt jest Haswell, a zatem obejmuje BMI2 również.)
Bitsets nie wydaje się właściwe do tego celu. Zapewniają łatwy dostęp do poszczególnych bitów zestawu, ale nie są ciągłymi zakresami elementów. Twoja metoda wygląda dla mnie dobrze. – Barmar
Już sprawiłeś, że jest "przyjazny dla użytkownika" poprzez zawinięcie go w funkcję. – imallett
Czy dane wyjściowe dla 'GetGroup (2, 5,3) nie powinny być' 4 (0b100) '? W GNU calc otrzymuję '(102 >> (5-3 + 1)) & ~ ((~ 0) <<3)' ->' 4/* 0b100 * /. (Po użyciu 'base2 (2)' do ustawienia wyjścia pomocniczego base to binary.) –