Comment feriez-vous cela en C? (Exemple: 10110001 devient 10001101 si nous devions refléter 8 bits). Y a-t-il des instructions sur certains processeurs qui simplifieraient cette tâche? P>
12 Réponses :
Le moyen naïf / lent / simple consiste à extraire le bit bas de l'entrée et à la déplacer dans une autre variable qui accumule une valeur de retour. pour d'autres types, le nombre de bits de stockage est le compilateurs malheureusement ne reconnaissez pas cette boucle comme un bit-inverse et l'optimise pour bras Tailleof (entrée) * Char_bit code>, mais cela inclut des bits de rembourrage potentiels qui ne font pas partie de la valeur. Les types de largeur fixe sont une bonne idée ici. P>
+ = code> au lieu de
| = code> fait que GCC compile plus efficacement pour X86 (en utilisant x86 instruction de changement de vitesse et d'ajout, LEA). Bien sûr, il y a beaucoup de façons plus rapides à inverser; voir les autres réponses. Cette boucle est bonne pour la petite taille de code (pas de gros masques), mais sans aucun avantage sans avantage. P>
rbit code> ou autre chose. (Voyez- sur l'explorateur Godbolt compilateur ) p> p>
Ne pas retourner votre résultat?
void code> fonction avec
retour code>? :-)
C'est en fait appelé "retournement de bits" et est couramment fait dans la brouillage de la FFT. La voie O (log n) est (pour un maximum de 32 bits): Peut-être que cette petite "visualisation" aide: Eh bien, si nous faisons l'art ASCII, voici le mien : P>
Un exemple de la première mission, avec un uint8_t code> exemple: p>
7 6 5 4 3 2 1 0
X X X X
6 7 4 5 2 3 0 1
\ X / \ X /
X X X X
/ X \ / X \
4 5 6 7 0 1 2 3
\ \ \ X / / /
\ \ X X / /
\ X X X /
X X X X
/ X X X \
/ / X X \ \
/ / / X \ \ \
0 1 2 3 4 5 6 7
Vous ferez probablement mieux d'utiliser un uint_fast32_t code> pour
x code> plutôt qu'un type signé (pouvant être inférieur à 32 bits de large). Ces changements sont UB sur des types signés.
@TOBYSpeight Yeah, j'ai copié cela du code C ++ où le type était uint32_t code> à l'origine et il y avait des surcharges pour différentes largeurs. Il prédate
uint_fast32_t code> étant disponible dans le compilateur.
@TOBYSpeight: Le passage d'un entiers signé est défini la mise en œuvre, pas i> UB. (Tant que le décompte de changement de vitesse est plus petit que la largeur de type, bien sûr identique à celle de non signé.) C'est un changement de droite arithmétique sur toutes les implémentations que je suis au courant, mais ce sont toutes des machines complémentaires de 2 2. C permet également au changement de vitesse logique. Mais de toute façon, oui, uint32_t code> est un bon choix et les quarts de droite arithmétiques étaient un bug.
par riche Schroeppel dans ce MIT Memo (si vous Peut lire au-delà de l'assembleur), les éléments suivants vont inverser les bits d'un octet 8 bits à condition que vous disposez de 64 bits arithmétiques disponibles: Quel type de ventille est sorti (le multiplie), les sélectionne (le et) puis les rétrécit en arrière (le module). p> est-ce une quantité de 8 bits que vous avez? p> p>
Bien que très intelligent, sur de nombreuses plateformes divisent ( / code>) et modulo (
% code>) sont des opérations coûteuses et multi-cycle, surtout si ce n'est pas une puissance de 2 que le Le compilateur peut optimiser dans une opération de masque de bits.
Ceci est intelligent mais doit être au moins 20 fois plus lent que l'approche de la table de recherche évidente ..
@R: dépend de votre CPU. Je parie que c'est trois cycles sur un Intel moderne, qui sont tous agréables aux parties parallèles du pipeline, tandis qu'une approche basée sur une table présente l'inconvénient majeur de, au mieux, occupant un cache précieux et, au pire, causant un stand de pipeline tandis que la mémoire est accessible.
@R.: % 1023 CODE> coûte environ 2 multiplie + un sous pour le faire en termes de
x - (x / 1023) * 1023 code>, en utilisant un inverse multiplicatif à point fixe Pour le
/ 1023 code> ( Pourquoi GCC utilise-t-il la multiplication par un nombre étrange dans la mise en œuvre de la division entière? ). GCC et SLIG choisissent de faire le multiplier par 1023 avec Shift / SUB, car il est proche d'une puissance de 2. Sur un X86 moderne (avec multiplie à 3 cycles), il ressemble à une latence de 13 cycle pour le tout, suite à la chaîne de DEP. à travers le
imul code> et ainsi de suite. Bon ILP, mais une recherche de table aurait mieux ILP.
( Lien Godbolt pour commentaire précédent ) Bien sûr, si vous ajustez le X86 moderne, vous utiliseriez SSSE3 PSHUFB CODE> Pour faire des recherches 4 bits parallèles et un bit-inverser un ensemble de 32 bits dans quelques mélanges.
Je pense que je ferais une table de recherche de Bitpatterns 0-255. Lisez chaque octet et avec la table de recherche inverse que l'octet et ensuite arrangez les octets résultants de manière appropriée. P>
La chose vraiment cool est qu'une recherche de table de 8 bits peut être effectuée dans une seule instruction (XLAT) dans l'ensemble Intel X86. Pas l'une des instructions les plus rapides, mais elle le fait dans une seule instruction relativement rapide! :-)
L'approche la plus rapide est presque sûre d'être une table de recherche: ou si vous pouvez vous permettre de fournir 128k de données de table (en vous permettant, je veux dire l'utilisation du cache de la CPU, pas la mémoire principale ou la mémoire virtuelle UTILISATION), Utilisez des unités 16 bits: P> out[0]=lut[in[1]];
out[1]=lut[in[0]];
quint64 mirror(quint64 a,quint8 l=64) { quint64 b=0; for(quint8 i=0;i<l;i++) { b|=(a>>(l-i-1))&((quint64)1<<i); } return b; } This function mirroring less then 64 bits. For instance it can mirroring 12 bits.quint64 and quint8 are defined in Qt. But it possible redefine it in anyway.
J'ai également juste compris une solution minimale pour mettre en miroir 4 bits (un grignotant) dans un espace temporaire de 16 bits.
mirr = ( (orig * 0x222) & 0x1284 ) % 63
int mirror (int input) {// return bit mirror of 8 digit number int tmp2; int out=0; for (int i=0; i<8; i++) { out = out << 1; tmp2 = input & 0x01; out = out | tmp2; input = input >> 1; } return out; }
S'il vous plaît ajouter des explications.
Même boucle une réponse de Simone, mais pour 8 bits et avec différents noms var. En fait, l'ordre d'exploitation différent de la réponse de Simone est un bug.
presque un duplicata de algorithme le plus efficace pour l'inversion de bits (de MSB-> LSB au LSB-> MSB) en C (qui a beaucoup de réponses, y compris une réponse AVX2 pour l'inverser tous les 8 bits de charratage) . p>
Les bits d'enregistrement AVX2 inverser strong> montre comment faire cela pour un vecteur emballé d'éléments 32 bits. Le même code porté sur des vecteurs de 128 bits compilerait simplement avec AVX. P>
Il est toujours bon pour un seul Int 32 bits car x86 a une excursion ronde très efficace entre INTEGER et Vector Regs: pshufb code> (
_mm_shauffle_epi8 code>) peut être utilisé comme nibble lut pour faire 16 recherches en parallèle. Vous n'avez besoin que de 8 recherches pour les 8 grignotants dans un entier unique 32 bits, mais le problème réel divise les octets d'entrée en grignotins séparés (avec leur moitié supérieure à zéro zéro). C'est fondamentalement le même problème que pour
PSHUFB CODE> POPCOUNT BASED. P>
int Bitrev = _mm_cvtsi128_si32 (RBA32 (_mm_cvtsi32_si128 (entrée))); Code>. Cela coûte seulement 2 instructions supplémentaires
movd code> pour obtenir un entier d'un registre entier dans XMM et en arrière. (Latence ronde = 3 cycles sur un processeur Intel comme Haswell.) P>
bras: h3>
rbit code> a une latence à cycle unique et fait un entier entièrement 32 bits dans une instruction. P>
Ce serait mieux comme une modification de La réponse que vous référencez ; Cela ne reste pas vraiment seul comme une réponse.
@Petercordes ajouté comme commentaire
Merci d'avoir aidé à faire trop de pile mieux; C'est un bon ajout à cette réponse.
Si vous êtes intéressé par une approche plus donc dans Une fonction C utilisant gnu armv7a code>, j'ai trouvé le
rbit code>
commande. ASM étendu code>
Je pourrais utiliser: p> uint32_t bit_reverse32(uint32_t inp32)
{
uint32_t out = 0;
asm("RBIT %0, %1" : "=r" (out) : "r" (inp32));
return out;
}
À quoi la plupart des gens ne considèrent pas mon approche ni aussi élégante ni efficace: elle vise à être portable et d'une manière ou d'une autre "" antérieure ". Cette fonction tire le moins important de la Source Bistring em> Bien sûr, il peut y avoir des instructions spécifiques à la CPU (ou des ensembles d'instructions) pouvant être utilisés à la place de mon code CLAIR C. p> mais que cela ne serait pas "langage C" mais plutôt des instructions de montage dans une emballage C. p> p> s code> et le pousse comme le bit le plus significatif dans la destination bitstring em>
d code>. p> << P> Vous pouvez remplacer le type de données code> non signé avec tout ce qui convient à votre cas, à partir de
non signé caractère code> (
char_bit code> bits, généralement 8) à
non signé long long code> (128 bits dans des processeurs modernes 64 bits). P>
Si vous visez Portable, utilisez char_bit code> au lieu de codage rigide
8 code>. Certains DSP modernes sont adressables par Word et ont ainsi 16, 24, voire 32 bits
CHAR CODE>, ce n'est donc pas seulement une question de machines héritées avec des octets de 9 bits ou autre.
"Mirror" est un mot OK, mais la plupart des gens l'appelleraient probablement «reversement de bits».
@Gregs: Merci, cela explique pourquoi j'ai eu du mal à googler.
À partir d'une réponse de liaison supprimée uniquement: Graphics.stanford.edu/~Seander/bithacks. HTML # BITREEDESSOBVIO US (quelques méthodes plus efficaces sont répertoriées également). Sur le X86 moderne, vous voudriez probablement utiliser SSSE3
pshufb code> en tant que nibble parallèle lut. (Idemble pour toute autre ISA SIMD avec un byte shuffle.) ARM a une instruction
rbit code> qui effectue toute la tâche dans une instruction efficace.