(src >> start) & ((1 << len)-1)
ist eine Möglichkeit, die Extraktion von len
Bits auszudrücken, bei start
beginnen. (In diesem Fall ist start
das LSB des gewünschten Bereichs. Ihre Funktion benötigt das MSB als Eingabe.) Dieser Ausdruck stammt von Wikipedia's article on the x86 BMI1 instruction set extensions.
Beide Möglichkeiten zur Herstellung der Maske sehen riskant aus, wenn len
die volle Breite des Typs ist. (Der Eckfall des Extrahierens aller Bits). Verschiebungen um die volle Breite des Typs können entweder null oder unverändert ergeben. . (IIRC, es ruft eigentlich nicht definiertes Verhalten, aber dies ist in der Praxis, was passiert, x86 zum Beispiel Masken zählt die Umstellung auf den 0-31 Bereich nach unten (für 32-Bit-Verschiebungen) mit 32-Bit ints.
Wenn 1 < < 32 erzeugt 1, dann 1-1 = 0, so wird das Ergebnis gleich Null sein.
Wenn ~ 0 < < 32 erzeugt ~ 0, eher als 0 ist, wird die Maske gleich Null sein.
Ich denke, von Ihren Beispielen wollen Sie die Bits [iStartPos : iStartPos - iNumOfBites]
, wo Bits von Null nummeriert sind.
Die Hauptsache, die ich in Ihrer Funktion ändern würde, ist die Benennung der Funktion und Variablen, und fügen Sie einen Kommentar hinzu.
bitResult
ist die Eingabe für die Funktion; Verwenden Sie nicht "result" in seinem Namen.
iStartPos
ok, aber ein wenig ausführlich
iNumOfBites
Computer haben Bits und Bytes. Wenn Sie mit Bissen zu tun haben, benötigen Sie einen Arzt (oder einen Zahnarzt).
Auch sollte der Rückgabetyp wahrscheinlich unsigned
sein.
// 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);
}
Wenn Ihr Start-Position-Parameter war der lsb, anstatt msb, würde der Ausdruck einfacher sein, und würde der Code kleiner und schneller sein (es sei denn, dass nur zusätzliche Arbeit für den Anrufer macht). Mit LSB als Parameter ist BitExtract 7 Anweisungen, im Gegensatz zu 9, wenn es MSB ist (auf x86-64, gcc 5.2).
Es gibt auch eine Maschinenanweisung (eingeführt mit Intel Haswell und AMD Piledriver), die diese Operation ausführt. Sie werden etwas kleineren und etwas schnelleren Code bekommen, indem Sie ihn benutzen. Es verwendet auch die LSB, len Position Konvention, nicht MSB, so erhalten Sie kürzere Code mit LSB als Argument.
Intel-CPUs kennen nur die Version, die das sofortige Laden eines Registers in ein Register erfordert. Wenn also die Werte Kompilierzeitkonstanten sind, spart es nicht viel im Vergleich zu einfachem Verschieben und Maskieren. e.g. see this post about using it or pextr for RGB32 -> RGB16. Und natürlich ist es egal, ob der Parameter das MSB oder LSB des gewünschten Bereichs ist, wenn start und len beide Kompilierzeitkonstanten sind.
Nur implementiert AMD eine Version von bextr
, der die Steuermaske als eine unmittelbare Konstante haben, aber leider scheint es, gcc 5.2 nicht die sofortige Ausführung für Code verwenden, der die intrinsische verwendet (auch bei -march=bdver2
(dh Bulldozer v2 aka piledriver). (Es wird generate bextr with an immediate argument on its own in some cases mit -march=bdver2
.)
ich tested it out on godbolt zu sehen, welche Art von Code, den Sie mit oder ohne 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
}
bekommen es eine zusätzliche Anweisung (a movzx
) nehmen würde implementieren a (msb-len+1)&0xff
Sicherheitsprüfung, um zu vermeiden, dass das Startbyte in das Längenbyte überläuft. Ich habe es weggelassen, weil es Unsinn ist, nach einem Startbit außerhalb des Bereichs von 0 bis 31 zu fragen, ganz zu schweigen von dem Bereich von 0 bis 255. Da es nicht zum Absturz kommt, gib einfach ein anderes Unsinnsergebnis zurück, da ist nicht viel Sinn.
Wie auch immer, spart bext
ziemlich viele Anweisungen (wenn BMI2 shlx
/shrx
entweder nicht verfügbar ist! -march=native
auf Godbolt ist Haswell, und schließt somit BMI2 auch.)
Bitsets scheinen nicht geeignet für diese. Sie bieten einfachen Zugriff auf einzelne Bits des Satzes, aber nicht zusammenhängende Bereiche von Elementen. Ihre Methode sieht gut aus für mich. – Barmar
Sie haben es bereits benutzerfreundlich gemacht, indem Sie es in eine Funktion eingepackt haben. – imallett
Sollte die Ausgabe für 'GetGroup (2, 5,3)' nicht '4 (0b100)' sein? In GNU calc bekomme ich '(102 >> (5-3 + 1)) und ~ ((~ 0) <<3)' ->' 4/* 0b100 */'. (Nach der Verwendung von' base2 (2) ', um den sekundären Ausgang zu setzen base to binary.) –