2015-11-05 9 views
7

Ich habe die folgende Funktion:
Bits Erhalten von Byte

int GetGroup(unsigned bitResult, int iStartPos, int iNumOfBites) 
{ 
return (bitResult >> (iStartPos + 1- iNumOfBites)) & ~(~0 << iNumOfBites); 
} 

Die Funktion gibt Gruppe von Bits aus einem Byte.
das heißt, wenn bitResult=102 (01100110)2, iStartPos=5, iNumOfBites=3
Ausgang: 2 (10)2
Für iStartPos=7, iNumOfBites=4
Ausgang: 3 (0110)2

Ich bin auf der Suche nach besserem Weg/"freundlich", das zu tun, das heißt mit bitset oder so ähnlich.
Irgendwelche Vorschläge?

+5

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

+7

Sie haben es bereits benutzerfreundlich gemacht, indem Sie es in eine Funktion eingepackt haben. – imallett

+0

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.) –

Antwort

3
(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.)

+0

Was wäre der Effekt, dies inline zu machen, würde es dadurch noch schneller werden und möglicherweise mehr lokalisierte Optimierungen erlauben? –

+0

@RichardChambers: wie Sie von der godbolt Ausgabe sehen können, wenn pos oder len Kompilierzeit Konstanten sind, wird es deutlich effizienter. Ansonsten werden nur ein oder zwei 'mov' Anweisungen und natürlich der Funktionsaufruf-Overhead gespeichert. Inlining ist vielleicht auf x86 (vs. x86-64) wertvoller, weil x86 einen langsameren Parameter-on-the-Stack-ABI hat. –

1
pragma pack(push, 1) 
struct Bit 
{ 
union 
    { 
    uint8_t _value; 
    struct { 
     uint8_t _bit0:0; 
     uint8_t _bit1:0; 
     uint8_t _bit2:0; 
     uint8_t _bit3:0; 
     uint8_t _bit4:0; 
     uint8_t _bit5:0; 
     uint8_t _bit6:0; 
     uint8_t _bit7:0; 
     }; 
    }; 
}; 
#pragma pack(pop, 1) 
typedef Bit bit; 

struct B 
{ 
    union 
    { 
      uint32_t _value; 
      bit bytes[1]; // 1 for Single Byte 
    }; 
}; 

Mit einer Struktur und Vereinigung Sie die Struct B _value auf Ihr Ergebnis einstellen, dann den Zugriff Byte [0] ._ Bit0 bis Byte [0] ._ bit7 für jeden 0 oder 1 und umgekehrt. Setze jedes Bit, und das Ergebnis wird im _value sein.

+0

Dies hilft nicht beim Extrahieren einer zusammenhängenden Gruppe von Bits, zB um die grüne Komponente aus einem gepackten rgb-Pixelwert zu holen. Siehe https: // B. gcc.gnu.org/ml/gcc/2014-02/msg00202.html –

+0

Das würde undefiniertes Verhalten für C++ aufrufen: http://stackoverflow.com/questions/11373203/accessing-inactive-union-member-undefined – Jens

1

Ich würde wahrscheinlich etwas wie das Folgende tun, um zusätzlichen Schutz um Fehler in Argumenten zu bieten und das Ausmaß der Verschiebung zu reduzieren.

Ich bin nicht sicher, ob ich die Bedeutung der Argumente verstanden habe, die Sie verwenden, so dass dies ein wenig Feinabstimmung erfordern kann.

Und ich bin mir nicht sicher, ob dies notwendigerweise effizienter ist, da es im Interesse der Sicherheit eine Reihe von Entscheidungen und Reichweitenüberprüfungen gibt.

/* 
* Arguments: const unsigned bitResult byte containing the bit field to extract 
*    const int iStartPos zero based offset from the least significant bit 
*    const int iNumOfBites number of bits to the right of the starting position 
* 
* Description: Extract a bitfield beginning at the specified position for the specified 
*    number of bits returning the extracted bit field right justified. 
*/ 
int GetGroup(const unsigned bitResult, const int iStartPos, const int iNumOfBites) 
{ 
    // masks to remove any leading bits that need to disappear. 
    // we change starting position to be one based so the first element is unused. 
    const static unsigned bitMasks[] = {0x01, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff}; 

    int iStart = (iStartPos > 7) ? 8 : iStartPos + 1; 
    int iNum = (iNumOfBites > 8) ? 8 : iNumOfBites; 

    unsigned retVal = (bitResult & bitMasks[iStart]); 
    if (iStart > iNum) { 
     retVal >>= (iStart - iNum); 
    } 

    return retVal; 
}