10
votes

miroir bits d'un mot de 32 bits

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?


3 commentaires

"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 en tant que nibble parallèle lut. (Idemble pour toute autre ISA SIMD avec un byte shuffle.) ARM a une instruction rbit qui effectue toute la tâche dans une instruction efficace.


12 Réponses :


2
votes

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. xxx

pour d'autres types, le nombre de bits de stockage est Tailleof (entrée) * Char_bit , 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.

le + = au lieu de | = 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.

compilateurs malheureusement ne reconnaissez pas cette boucle comme un bit-inverse et l'optimise pour bras rbit ou autre chose. (Voyez- sur l'explorateur Godbolt compilateur )


2 commentaires

Ne pas retourner votre résultat?


void fonction avec retour ? :-)



13
votes

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): xxx pré>


Peut-être que cette petite "visualisation" aide:
Un exemple de la première mission, avec un uint8_t code> exemple: p> xxx pré>


Eh bien, si nous faisons l'art ASCII, voici le mien : 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


3 commentaires

Vous ferez probablement mieux d'utiliser un uint_fast32_t pour x 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 à l'origine et il y avait des surcharges pour différentes largeurs. Il prédate uint_fast32_t étant disponible dans le compilateur.


@TOBYSpeight: Le passage d'un entiers signé est défini la mise en œuvre, pas 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 est un bon choix et les quarts de droite arithmétiques étaient un bug.



3
votes

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: xxx

Quel type de ventille est sorti (le multiplie), les sélectionne (le et) puis les rétrécit en arrière (le module).

est-ce une quantité de 8 bits que vous avez?


5 commentaires

Bien que très intelligent, sur de nombreuses plateformes divisent ( / ) et modulo (% ) 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 coûte environ 2 multiplie + un sous pour le faire en termes de x - (x / 1023) * 1023 , en utilisant un inverse multiplicatif à point fixe Pour le / 1023 ( 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 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 Pour faire des recherches 4 bits parallèles et un bit-inverser un ensemble de 32 bits dans quelques mélanges.



0
votes

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.


1 commentaires

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! :-)



2
votes

L'approche la plus rapide est presque sûre d'être une table de recherche: xxx pré>

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]];


0 commentaires

0
votes
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.

0 commentaires

1
votes

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


0 commentaires

-2
votes
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;
}

2 commentaires

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.



3
votes

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


x86

sur x86 avec SSSE3 (core2 et plus tard, bulldozer et plus tard), pshufb ( _mm_shauffle_epi8 ) 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 POPCOUNT BASED.

Les bits d'enregistrement AVX2 inverser 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.

Il est toujours bon pour un seul Int 32 bits car x86 a une excursion ronde très efficace entre INTEGER et Vector Regs: int Bitrev = _mm_cvtsi128_si32 (RBA32 (_mm_cvtsi32_si128 (entrée))); . Cela coûte seulement 2 instructions supplémentaires movd pour obtenir un entier d'un registre entier dans XMM et en arrière. (Latence ronde = 3 cycles sur un processeur Intel comme Haswell.)


bras:

rbit a une latence à cycle unique et fait un entier entièrement 32 bits dans une instruction.


0 commentaires


2
votes

Si vous êtes intéressé par une approche plus intégrée forte>, lorsque j'ai travaillé avec un système armv7a code>, j'ai trouvé le rbit code> commande.

donc dans Une fonction C utilisant gnu 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;
}


0 commentaires

0
votes

À 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 ". xxx

Cette fonction tire le moins important de la Source Bistring s et le pousse comme le bit le plus significatif dans la destination bitstring d . << P> Vous pouvez remplacer le type de données non signé avec tout ce qui convient à votre cas, à partir de non signé caractère ( char_bit bits, généralement 8) à non signé long long (128 bits dans des processeurs modernes 64 bits).

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.

mais que cela ne serait pas "langage C" mais plutôt des instructions de montage dans une emballage C.


1 commentaires

Si vous visez Portable, utilisez char_bit au lieu de codage rigide 8 . Certains DSP modernes sont adressables par Word et ont ainsi 16, 24, voire 32 bits CHAR , ce n'est donc pas seulement une question de machines héritées avec des octets de 9 bits ou autre.