En C / C ++, quelle est la manière la plus simple d'inverser l'ordre des bits dans un octet?


110

Bien qu'il existe plusieurs façons d'inverser l'ordre des bits dans un octet, je suis curieux de savoir quelle est la "plus simple" à implémenter pour un développeur. Et en inversant je veux dire:

1110 -> 0111
0010 -> 0100

Ceci est similaire, mais pas un double de celui-ci question PHP.

Ceci est similaire, mais pas une copie de cette question C. Cette question demande la méthode la plus simple à mettre en œuvre par un développeur. Le «meilleur algorithme» concerne les performances de la mémoire et du processeur.


Utilisez l'assemblage en ligne. Mieux, placez la fonction dans une unité de traduction distincte. Avoir un module de langage d'assemblage pour chaque plate-forme cible. Laissez le processus de construction choisir les modules.
Thomas Matthews

@Andreas Simplest implementation
nathan

Réponses:


102

Si vous parlez d'un seul octet, une recherche de table est probablement le meilleur choix, sauf si pour une raison quelconque vous n'avez pas 256 octets disponibles.


12
Si nous parlons de quelque chose de simple à implémenter sans copier une solution toute faite, la création de la table de recherche nécessite toujours une autre solution. (Bien sûr, on pourrait le faire à la main, mais c'est sujet aux erreurs et prend du temps…)
Arkku

7
Vous pouvez compresser le tableau en un peu moins de 256 octets si vous ignorez les palindromes.
wilhelmtell

8
@wilhelmtell - vous auriez besoin d'une table pour savoir lesquels sont les palindromes.
Mark Ransom

6
@wilhelmtell: Eh bien, pour écrire le script, il faut encore une autre solution, ce qui était mon point - une table de consultation est simple à utiliser mais pas simple à créer. (Sauf en copiant une table de recherche prête à l'emploi, mais alors on pourrait tout aussi bien copier n'importe quelle solution.) Par exemple, si la solution la plus «simple» est considérée comme celle qui pourrait être écrite sur papier lors d'un examen ou d'un entretien, je ne le ferais pas commencer à créer une table de recherche à la main et faire en sorte que le programme le fasse inclurait déjà une solution différente (qui serait plus simple seule que celle incluant à la fois la table et la table).
Arkku

4
@Arkku ce que je voulais dire, c'est écrire un script qui génère la table des 256 premiers octets et leur mappage inversé. Oui, vous êtes de retour à l'écriture de la fonction inverse, mais maintenant dans votre langage de script préféré, et cela peut être aussi méchant que vous le souhaitez - vous allez le jeter dès que c'est fait et vous l'avez exécuté une fois. Avoir la sortie du script code C, même: unsigned int rtable[] = {0x800, 0x4000, ...};. Ensuite, jetez le script et oubliez que vous l'avez jamais eu. Il est beaucoup plus rapide à écrire que le code C ++ équivalent, et il ne s'exécutera qu'une seule fois, donc vous obtenez le runtime O (1) dans votre code C ++.
wilhelmtell

227

Cela devrait fonctionner:

unsigned char reverse(unsigned char b) {
   b = (b & 0xF0) >> 4 | (b & 0x0F) << 4;
   b = (b & 0xCC) >> 2 | (b & 0x33) << 2;
   b = (b & 0xAA) >> 1 | (b & 0x55) << 1;
   return b;
}

Tout d'abord, les quatre bits de gauche sont échangés avec les quatre bits de droite. Ensuite, toutes les paires adjacentes sont permutées, puis tous les bits simples adjacents. Cela entraîne un ordre inversé.


26
Raisonnablement court et rapide, mais pas simple.
Mark Ransom

3
Cette approche se généralise également proprement pour effectuer un échange d'octets pour endianness.
Boojum

2
Ce n'est pas l'approche la plus simple, mais je l'aime +1.
nathan

7
Oui, c'est simple. C'est une sorte d'algorithme de division et de conquête. Excellent!
kiewic

Est-ce plus rapide que la méthode suggérée par @Arkku ci-dessous?
qed

123

Je pense qu'une table de consultation doit être l'une des méthodes les plus simples. Cependant, vous n'avez pas besoin d'une table de recherche complète.

//Index 1==0b0001 => 0b1000
//Index 7==0b0111 => 0b1110
//etc
static unsigned char lookup[16] = {
0x0, 0x8, 0x4, 0xc, 0x2, 0xa, 0x6, 0xe,
0x1, 0x9, 0x5, 0xd, 0x3, 0xb, 0x7, 0xf, };

uint8_t reverse(uint8_t n) {
   // Reverse the top and bottom nibble then swap them.
   return (lookup[n&0b1111] << 4) | lookup[n>>4];
}

// Detailed breakdown of the math
//  + lookup reverse of bottom nibble
//  |       + grab bottom nibble
//  |       |        + move bottom result into top nibble
//  |       |        |     + combine the bottom and top results 
//  |       |        |     | + lookup reverse of top nibble
//  |       |        |     | |       + grab top nibble
//  V       V        V     V V       V
// (lookup[n&0b1111] << 4) | lookup[n>>4]

C'est assez simple à coder et à vérifier visuellement.
En fin de compte, cela pourrait même être plus rapide qu'une table complète. Le bit arith est bon marché et la table tient facilement sur une ligne de cache.


10
C'est un excellent moyen de réduire la complexité de la solution de table. +1
e.James

3
Bien, mais vous donnera une cache manquer.
Johan Kotlinski

7
@kotlinski: qu'est-ce qui fera manquer un cache? Je pense que la version petite table peut être plus efficace en cache que la grande. Sur mon Core2, une ligne de cache a une largeur de 64 octets, la table complète s'étend sur plusieurs lignes, tandis que la table plus petite en tient facilement une seule ligne.
deft_code

4
@kotlinski: La localité temporelle est plus importante pour les hits de cache ou les stratégies de remplacement que la localité d' adresse
cfi

6
@Harshdeep: considérez les index codés en binaire des entrées de la table. index b0000 (0) -> b0000 (0x0) ennuyeux; b0001(1) -> b1000(0x8), b0010(2) -> b0100(0x4), b1010(10) -> b0101(0x5). Vous voyez le modèle? C'est assez simple pour que vous puissiez le calculer dans votre tête (si vous pouvez lire le binaire, sinon vous aurez besoin de papier pour le résoudre). Quant au saut, l'inversion d'un entier de 8 bits équivaut à inverser des parties de 4 bits puis à les échanger; Je revendique l'expérience et l'intuition (ou la magie).
deft_code

46

Voir les hacks twiddling bit pour de nombreuses solutions. Le copypast à partir de là est évidemment simple à mettre en œuvre. =)

Par exemple (sur un processeur 32 bits):

uint8_t b = byte_to_reverse;
b = ((b * 0x0802LU & 0x22110LU) | (b * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16;

Si par «simple à mettre en œuvre», on entend quelque chose qui peut être fait sans référence dans un examen ou un entretien d'embauche, alors le pari le plus sûr est probablement la copie inefficace des bits un par un dans une autre variable dans l'ordre inverse (déjà indiqué dans d'autres réponses ).


1
De votre URL: CPU 32 bits: b = ((b * 0x0802LU & 0x22110LU) | (b * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16;
Joshua

1
@Joshua: C'est aussi mon préféré. La mise en garde (comme indiqué sur la page liée) est qu'il doit être attribué ou converti en uint8_t ou il y aura des déchets dans les bits supérieurs.
Arkku

42

Puisque personne n'a publié de solution complète de recherche de table, voici la mienne:

unsigned char reverse_byte(unsigned char x)
{
    static const unsigned char table[] = {
        0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
        0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
        0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
        0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
        0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
        0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
        0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
        0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
        0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
        0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
        0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
        0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
        0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
        0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
        0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
        0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
        0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
        0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
        0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
        0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
        0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
        0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
        0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
        0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
        0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
        0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
        0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
        0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
        0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
        0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
        0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
        0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff,
    };
    return table[x];
}

2
Utile, merci. Il semble que ma méthode de changement de vitesse plus lente limitait les performances d'une application intégrée. Table placée en ROM sur un PIC (avec ajout du mot-clé rom).
flend


25
template <typename T>
T reverse(T n, size_t b = sizeof(T) * CHAR_BIT)
{
    assert(b <= std::numeric_limits<T>::digits);

    T rv = 0;

    for (size_t i = 0; i < b; ++i, n >>= 1) {
        rv = (rv << 1) | (n & 0x01);
    }

    return rv;
}

ÉDITER:

Je l'ai converti en modèle avec le nombre de bits optionnel


@nvl - corrigé. J'ai commencé à le construire comme modèle mais j'ai décidé à mi-chemin de ne pas le faire ... trop de & gt & lt
et et

Pour plus de pedenatry, remplacez sizeof(T)*8par sizeof(T)*CHAR_BITS.
Pillsy

6
@andand Pour une suspension supplémentaire, remplacez sizeof(T)*CHAR_BITpar std::numeric_limits<T>::digits(presque 4 ans de pédanterie plus tard).
Morwenn

1
Ça devrait être CHAR_BIT, non CHAR_BITS.
Xunie

1
il devrait être rv = (rv << 1) | (n & 0x01);
Vignesh

16

Deux lignes:

for(i=0;i<8;i++)
     reversed |= ((original>>i) & 0b1)<<(7-i);

ou en cas de problème avec la partie "0b1":

for(i=0;i<8;i++)
     reversed |= ((original>>i) & 1)<<(7-i);

"original" est l'octet que vous souhaitez inverser. "inversé" est le résultat, initialisé à 0.


14

Bien que probablement pas portable, j'utiliserais le langage d'assemblage.
De nombreux langages d'assemblage ont des instructions pour tourner un peu dans le drapeau de retenue et pour faire pivoter le drapeau de retenue dans le mot (ou l'octet).

L'algorithme est:

for each bit in the data type:
  rotate bit into carry flag
  rotate carry flag into destination.
end-for

Le code de langage de haut niveau pour cela est beaucoup plus compliqué, car C et C ++ ne prennent pas en charge la rotation pour porter et la rotation à partir de carry. Le drapeau de portage doit être modélisé.

Edit: langage d'assemblage par exemple

;  Enter with value to reverse in R0.
;  Assume 8 bits per byte and byte is the native processor type.
   LODI, R2  8       ; Set up the bit counter
Loop:
   RRC, R0           ; Rotate R0 right into the carry bit.
   RLC, R1           ; Rotate R1 left, then append carry bit.
   DJNZ, R2  Loop    ; Decrement R2 and jump if non-zero to "loop"
   LODR, R0  R1      ; Move result into R0.

7
Je pense que cette réponse est le contraire du simple. Non portable, assembleur et suffisamment complexe pour être écrit en pseudo-code au lieu de l'assembly réel.
deft_code

3
C'est assez simple. Je l'ai mis en pseudo-code parce que les mnémoniques d'assemblage sont spécifiques à une race de processeurs et qu'il existe de nombreuses races. Si vous le souhaitez, je peux modifier ceci pour afficher le langage d'assemblage simple.
Thomas Matthews

On pourrait voir si une optimisation du compilateur se simplifie en une instruction d'assemblage appropriée.
Sparky

12

Je trouve la solution suivante plus simple que les autres algorithmes de bidouillage que j'ai vus ici.

unsigned char reverse_byte(char a)
{

  return ((a & 0x1)  << 7) | ((a & 0x2)  << 5) |
         ((a & 0x4)  << 3) | ((a & 0x8)  << 1) |
         ((a & 0x10) >> 1) | ((a & 0x20) >> 3) |
         ((a & 0x40) >> 5) | ((a & 0x80) >> 7);
}

Il récupère chaque bit de l'octet et le décale en conséquence, du premier au dernier.

Explication:

   ((a & 0x1) << 7) //get first bit on the right and shift it into the first left position 
 | ((a & 0x2) << 5) //add it to the second bit and shift it into the second left position
  //and so on

Belle! Mon préféré jusqu'à présent.
Nick Rameau

C'est certes simple, mais il faut souligner que le temps d'exécution est O (n) plutôt que O (log₂ n), où n est le nombre de bits (8, 16, 32, 64, etc.).
Todd Lehman

10

Le moyen le plus simple consiste probablement à parcourir les positions des bits dans une boucle:

unsigned char reverse(unsigned char c) {
   int shift;
   unsigned char result = 0;
   for (shift = 0; shift < CHAR_BIT; shift++) {
      if (c & (0x01 << shift))
         result |= (0x80 >> shift);
   }
   return result;
}

c'est CHAR_BIT, sans un 's'
ljrk

Pourquoi utiliser CHAR_BITquand vous supposez charavoir 8 bits?
chqrlie

6

Vous pourriez être intéressé par std::vector<bool>(c'est-à-dire en bits) etstd::bitset

Ce devrait être le plus simple demandé.

#include <iostream>
#include <bitset>
using namespace std;
int main() {
  bitset<8> bs = 5;
  bitset<8> rev;
  for(int ii=0; ii!= bs.size(); ++ii)
    rev[bs.size()-ii-1] = bs[ii];
  cerr << bs << " " << rev << endl;
}

D'autres options peuvent être plus rapides.

EDIT: je vous dois une solution utilisant std::vector<bool>

#include <algorithm>
#include <iterator>
#include <iostream>
#include <vector>
using namespace std;
int main() {
  vector<bool> b{0,0,0,0,0,1,0,1};
  reverse(b.begin(), b.end());
  copy(b.begin(), b.end(), ostream_iterator<int>(cerr));
  cerr << endl;
}

Le deuxième exemple nécessite l'extension c ++ 0x (pour initialiser le tableau avec {...}). L'avantage d'utiliser un bitsetou un std::vector<bool>(ou unboost::dynamic_bitset ) est que vous n'êtes pas limité aux octets ou aux mots mais que vous pouvez inverser un nombre arbitraire de bits.

HTH


En quoi le jeu de bits est-il plus simple qu'un pod ici? Montrez le code, ou non.
wilhelmtell

En fait, je pense que ce code inversera l'ensemble de bits, puis le ramènera à son original. Changer ii! = Taille (); à ii <size () / 2; et ça fera un meilleur travail =)
Viktor Sehr

(@ viktor-sehr non, ce ne sera pas le cas, rev est différent de bs). Quoi qu'il en soit, je n'aime pas la réponse moi-même: je pense que c'est un cas où l'arithmétique binaire et les opérateurs de décalage sont mieux adaptés. Cela reste le plus simple à comprendre.
baol

Et si vous std::vector<bool> b = { ... }; std::vector<bool> rb ( b.rbegin(), b.rend()); utilisiez directement des itérateurs inversés?
MSalters

@MSalters J'aime son immuabilité.
baol

6

Dans le cas très limité d' une entrée constante de 8 bits , cette méthode ne coûte ni mémoire ni CPU au moment de l'exécution:

#define MSB2LSB(b) (((b)&1?128:0)|((b)&2?64:0)|((b)&4?32:0)|((b)&8?16:0)|((b)&16?8:0)|((b)&32?4:0)|((b)&64?2:0)|((b)&128?1:0))

J'ai utilisé ceci pour ARINC-429 où l'ordre des bits (endianness) de l'étiquette est opposé au reste du mot. L'étiquette est souvent une constante, et classiquement en octal.

Voici comment je l'ai utilisé pour définir une constante, car la spécification définit cette étiquette comme big-endian 205 octal.

#define LABEL_HF_COMM MSB2LSB(0205)

Plus d'exemples:

assert(0b00000000 == MSB2LSB(0b00000000));
assert(0b10000000 == MSB2LSB(0b00000001));
assert(0b11000000 == MSB2LSB(0b00000011));
assert(0b11100000 == MSB2LSB(0b00000111));
assert(0b11110000 == MSB2LSB(0b00001111));
assert(0b11111000 == MSB2LSB(0b00011111));
assert(0b11111100 == MSB2LSB(0b00111111));
assert(0b11111110 == MSB2LSB(0b01111111));
assert(0b11111111 == MSB2LSB(0b11111111));
assert(0b10101010 == MSB2LSB(0b01010101));

5

Il existe de nombreuses façons d'inverser les bits en fonction de ce que vous entendez par la «manière la plus simple».


Inverser par rotation

Probablement le plus logique, consiste à faire tourner l'octet tout en appliquant un masque sur le premier bit (n & 1):

unsigned char reverse_bits(unsigned char b)
{
    unsigned char   r = 0;
    unsigned        byte_len = 8;

    while (byte_len--) {
        r = (r << 1) | (b & 1);
        b >>= 1;
    }
    return r;
}

1) Comme la longueur d'un caractère unsigner est de 1 octet, ce qui est égal à 8 bits, cela signifie que nous allons scanner chaque bit while (byte_len--)

2) On vérifie d'abord si b est un peu à l'extrême droite avec (b & 1); si c'est le cas, nous positionnons le bit 1 sur r avec |et le déplaçons juste 1 bit vers la gauche en multipliant r par 2 avec(r << 1)

3) Ensuite, nous divisons notre caractère non signé b par 2 avec b >>=1pour effacer le bit situé à l'extrême droite de la variable b. Pour rappel, b >> = 1; équivaut à b / = 2;


Inverser en une seule ligne

Cette solution est attribuée à Rich Schroeppel dans la section Programming Hacks

unsigned char reverse_bits3(unsigned char b)
{
    return (b * 0x0202020202ULL & 0x010884422010ULL) % 0x3ff;
}

1) L'opération de multiplication (b * 0x0202020202ULL) crée cinq copies distinctes du modèle d'octets 8 bits à déplier en une valeur 64 bits.

2) L'opération ET (& 0x010884422010ULL) sélectionne les bits qui sont dans les positions correctes (inversées), par rapport à chaque groupe de bits de 10 bits.

3) Ensemble, les opérations multiplication et ET copient les bits de l'octet d'origine afin qu'ils apparaissent chacun dans un seul des ensembles de 10 bits. Les positions inversées des bits de l'octet d'origine coïncident avec leurs positions relatives dans tout ensemble de 10 bits.

4) La dernière étape (% 0x3ff), qui implique la division du module par 2 ^ 10 - 1 a pour effet de fusionner chaque ensemble de 10 bits (à partir des positions 0-9, 10-19, 20-29, ...) dans la valeur 64 bits. Ils ne se chevauchent pas, de sorte que les étapes d'addition sous-jacentes à la division du module se comportent comme des opérations OR.


Solution Diviser et Conquérir

unsigned char reverse(unsigned char b) {
   b = (b & 0xF0) >> 4 | (b & 0x0F) << 4;
   b = (b & 0xCC) >> 2 | (b & 0x33) << 2;
   b = (b & 0xAA) >> 1 | (b & 0x55) << 1;
   return b;
}

C'est la réponse la plus votée et malgré quelques explications, je pense que pour la plupart des gens, il est difficile de visualiser ce que signifie vraiment 0xF0, 0xCC, 0xAA, 0x0F, 0x33 et 0x55.

Il ne profite pas de '0b' qui est une extension GCC et est inclus depuis la norme C ++ 14, sortie en décembre 2014, donc un peu après cette réponse datant d'avril 2010

Les constantes entières peuvent être écrites sous forme de constantes binaires, constituées d'une séquence de chiffres «0» et «1», précédés de «0b» ou «0B». Ceci est particulièrement utile dans les environnements qui fonctionnent beaucoup au niveau du bit (comme les microcontrôleurs).

Veuillez vérifier les extraits de code ci-dessous pour vous souvenir et comprendre encore mieux cette solution où nous nous déplaçons de moitié par moitié:

unsigned char reverse(unsigned char b) {
   b = (b & 0b11110000) >> 4 | (b & 0b00001111) << 4;
   b = (b & 0b11001100) >> 2 | (b & 0b00110011) << 2;
   b = (b & 0b10101010) >> 1 | (b & 0b01010101) << 1;
   return b;
}

NB: C'est >> 4parce qu'il y a 8 bits dans 1 octet, ce qui est un caractère non signé donc nous voulons prendre l'autre moitié, et ainsi de suite.

Nous pourrions facilement appliquer cette solution à 4 octets avec seulement deux lignes supplémentaires et en suivant la même logique. Étant donné que les deux masques se complètent, nous pouvons même utiliser ~ pour changer de bits et économiser de l'encre:

uint32_t reverse_integer_bits(uint32_t b) {
   uint32_t mask = 0b11111111111111110000000000000000;
   b = (b & mask) >> 16 | (b & ~mask) << 16;
   mask = 0b11111111000000001111111100000000;
   b = (b & mask) >> 8 | (b & ~mask) << 8;
   mask = 0b11110000111100001111000011110000;
   b = (b & mask) >> 4 | (b & ~mask) << 4;
   mask = 0b11001100110011001100110011001100;
   b = (b & mask) >> 2 | (b & ~mask) << 2;
   mask = 0b10101010101010101010101010101010;
   b = (b & mask) >> 1 | (b & ~mask) << 1;
   return b;
}

[C ++ uniquement] Inverser tout non signé (modèle)

La logique ci-dessus peut être résumée avec une boucle qui fonctionnerait sur n'importe quel type de non signé:

template <class T>
T reverse_bits(T n) {
    short bits = sizeof(n) * 8; 
    T mask = ~T(0); // equivalent to uint32_t mask = 0b11111111111111111111111111111111;

    while (bits >>= 1) {
        mask ^= mask << (bits); // will convert mask to 0b00000000000000001111111111111111;
        n = (n & ~mask) >> bits | (n & mask) << bits; // divide and conquer
    }

    return n;
}

Essayez-le vous-même avec l'inclusion de la fonction ci-dessus:

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

template <class T>
void print_binary(T n)
{   T mask = 1ULL << ((sizeof(n) * 8) - 1);  // will set the most significant bit
    for(; mask != 0; mask >>= 1) putchar('0' | !!(n & mask));
    putchar('\n');
}

int main() {
    uint32_t n = 12;
    print_binary(n);
    n = reverse_bits(n); 
    print_binary(n);
    unsigned char c = 'a';
    print_binary(c);
    c = reverse_bits(c);
    print_binary(c);
    uint16_t s = 12;
    print_binary(s);
    s = reverse_bits(s);
    print_binary(s);
    uint64_t l = 12;
    print_binary(l);
    l = reverse_bits(l);
    print_binary(l);
    return 0;
}

Inverser avec asm volatile

Enfin, si le plus simple signifie moins de lignes, pourquoi ne pas essayer l'assemblage en ligne?

Vous pouvez tester l'extrait de code ci-dessous en ajoutant -masm=intellors de la compilation:

unsigned char reverse_bits(unsigned char c) {
    __asm__ __volatile__ (R"(
        mov cx, 8       
    daloop:                   
        ror di          
        adc ax, ax      
        dec cx          
        jnz short daloop  
    ;)");
}

Explications ligne par ligne:

        mov cx, 8       ; we will reverse the 8 bits contained in one byte
    daloop:             ; while loop
        shr di          ; Shift Register `di` (containing value of the first argument of callee function) to the Right
        rcl ax          ; Rotate Carry Left: rotate ax left and add the carry from shr di, the carry is equal to 1 if one bit was "lost" from previous operation 
        dec cl          ; Decrement cx
        jnz short daloop; Jump if cx register is Not equal to Zero, else end loop and return value contained in ax register

3

Recherche de table ou

uint8_t rev_byte(uint8_t x) {
    uint8_t y;
    uint8_t m = 1;
    while (m) {
       y >>= 1;
       if (m&x) {
          y |= 0x80;
       }
       m <<=1;
    }
    return y;
}

Éditer

Recherchez ici d'autres solutions qui pourraient mieux fonctionner pour vous


3

une implémentation plus lente mais plus simple:

static int swap_bit(unsigned char unit)
{
    /*
     * swap bit[7] and bit[0]
     */
    unit = (((((unit & 0x80) >> 7) ^ (unit & 0x01)) << 7) | (unit & 0x7f));
    unit = (((((unit & 0x80) >> 7) ^ (unit & 0x01))) | (unit & 0xfe));
    unit = (((((unit & 0x80) >> 7) ^ (unit & 0x01)) << 7) | (unit & 0x7f));

    /*
     * swap bit[6] and bit[1]
     */
    unit = (((((unit & 0x40) >> 5) ^ (unit & 0x02)) << 5) | (unit & 0xbf));
    unit = (((((unit & 0x40) >> 5) ^ (unit & 0x02))) | (unit & 0xfd));
    unit = (((((unit & 0x40) >> 5) ^ (unit & 0x02)) << 5) | (unit & 0xbf));

    /*
     * swap bit[5] and bit[2]
     */
    unit = (((((unit & 0x20) >> 3) ^ (unit & 0x04)) << 3) | (unit & 0xdf));
    unit = (((((unit & 0x20) >> 3) ^ (unit & 0x04))) | (unit & 0xfb));
    unit = (((((unit & 0x20) >> 3) ^ (unit & 0x04)) << 3) | (unit & 0xdf));

    /*
     * swap bit[4] and bit[3]
     */
    unit = (((((unit & 0x10) >> 1) ^ (unit & 0x08)) << 1) | (unit & 0xef));
    unit = (((((unit & 0x10) >> 1) ^ (unit & 0x08))) | (unit & 0xf7));
    unit = (((((unit & 0x10) >> 1) ^ (unit & 0x08)) << 1) | (unit & 0xef));

    return unit;
}

3

Cela peut-il être une solution rapide?

int byte_to_be_reversed = 
    ((byte_to_be_reversed>>7)&0x01)|((byte_to_be_reversed>>5)&0x02)|      
    ((byte_to_be_reversed>>3)&0x04)|((byte_to_be_reversed>>1)&0x08)| 
    ((byte_to_be_reversed<<7)&0x80)|((byte_to_be_reversed<<5)&0x40)|
    ((byte_to_be_reversed<<3)&0x20)|((byte_to_be_reversed<<1)&0x10);

Débarrassez-vous de la hâte d'utiliser une boucle for! mais les experts s'il vous plaît me dire si c'est efficace et plus rapide?


Le temps d'exécution est O (n) plutôt que O (log₂ n), où n est le nombre de bits (8, 16, 32, 64, etc.). Voir ailleurs pour les réponses qui s'exécutent en temps O (log₂ n).
Todd Lehman

2

Avant d'implémenter une solution algorithmique, vérifiez le langage d'assemblage pour quelle que soit l'architecture de processeur que vous utilisez. Votre architecture peut inclure des instructions qui gèrent des manipulations au niveau du bit comme celle-ci (et quoi de plus simple qu'une seule instruction d'assemblage?).

Si une telle instruction n'est pas disponible, je suggérerais de suivre l'itinéraire de la table de recherche. Vous pouvez écrire un script / programme pour générer la table pour vous, et les opérations de recherche seraient plus rapides que n'importe quel algorithme d'inversion de bits ici (au prix de devoir stocker la table de recherche quelque part).


2

Cette fonction simple utilise un masque pour tester chaque bit de l'octet d'entrée et le transférer dans une sortie décalée:

char Reverse_Bits(char input)
{    
    char output = 0;

    for (unsigned char mask = 1; mask > 0; mask <<= 1)
    {
        output <<= 1;

        if (input & mask)
            output |= 1;
    }

    return output;
}

Le masque doit être non signé, désolé.
luci88filter

1

Celui - ci est basé sur une BobStein-VisiBone fourni

#define reverse_1byte(b)    ( ((uint8_t)b & 0b00000001) ? 0b10000000 : 0 ) | \
                            ( ((uint8_t)b & 0b00000010) ? 0b01000000 : 0 ) | \
                            ( ((uint8_t)b & 0b00000100) ? 0b00100000 : 0 ) | \
                            ( ((uint8_t)b & 0b00001000) ? 0b00010000 : 0 ) | \
                            ( ((uint8_t)b & 0b00010000) ? 0b00001000 : 0 ) | \
                            ( ((uint8_t)b & 0b00100000) ? 0b00000100 : 0 ) | \
                            ( ((uint8_t)b & 0b01000000) ? 0b00000010 : 0 ) | \
                            ( ((uint8_t)b & 0b10000000) ? 0b00000001 : 0 ) 

J'aime beaucoup celui-ci car le compilateur gère automatiquement le travail pour vous, donc ne nécessite aucune ressource supplémentaire.

cela peut également être étendu à 16 bits ...

#define reverse_2byte(b)    ( ((uint16_t)b & 0b0000000000000001) ? 0b1000000000000000 : 0 ) | \
                            ( ((uint16_t)b & 0b0000000000000010) ? 0b0100000000000000 : 0 ) | \
                            ( ((uint16_t)b & 0b0000000000000100) ? 0b0010000000000000 : 0 ) | \
                            ( ((uint16_t)b & 0b0000000000001000) ? 0b0001000000000000 : 0 ) | \
                            ( ((uint16_t)b & 0b0000000000010000) ? 0b0000100000000000 : 0 ) | \
                            ( ((uint16_t)b & 0b0000000000100000) ? 0b0000010000000000 : 0 ) | \
                            ( ((uint16_t)b & 0b0000000001000000) ? 0b0000001000000000 : 0 ) | \
                            ( ((uint16_t)b & 0b0000000010000000) ? 0b0000000100000000 : 0 ) | \
                            ( ((uint16_t)b & 0b0000000100000000) ? 0b0000000010000000 : 0 ) | \
                            ( ((uint16_t)b & 0b0000001000000000) ? 0b0000000001000000 : 0 ) | \
                            ( ((uint16_t)b & 0b0000010000000000) ? 0b0000000000100000 : 0 ) | \
                            ( ((uint16_t)b & 0b0000100000000000) ? 0b0000000000010000 : 0 ) | \
                            ( ((uint16_t)b & 0b0001000000000000) ? 0b0000000000001000 : 0 ) | \
                            ( ((uint16_t)b & 0b0010000000000000) ? 0b0000000000000100 : 0 ) | \
                            ( ((uint16_t)b & 0b0100000000000000) ? 0b0000000000000010 : 0 ) | \
                            ( ((uint16_t)b & 0b1000000000000000) ? 0b0000000000000001 : 0 ) 

Je mettrais le bentre parenthèses au cas où il s'agirait d' une expression plus complexe qu'un seul nombre, et peut-être également renommer la macro REVERSE_BYTEen un indice que vous ne voulez probablement pas avoir une expression plus complexe (runtime). Ou faites-en une fonction en ligne. (Mais dans l'ensemble, j'aime cela comme étant assez simple pour que vous puissiez le faire de mémoire facilement avec très peu de chances d'erreur.)
Arkku

1

En supposant que votre compilateur autorise non signé long long :

unsigned char reverse(unsigned char b) {
  return (b * 0x0202020202ULL & 0x010884422010ULL) % 1023;
}

Découvert ici


1

Si vous utilisez un petit microcontrôleur et avez besoin d'une solution haute vitesse avec un faible encombrement, cela pourrait être des solutions. Il est possible de l'utiliser pour un projet C, mais vous devez ajouter ce fichier en tant que fichier assembleur * .asm, à votre projet C. Instructions: Dans le projet C, ajoutez cette déclaration:

extern uint8_t byte_mirror(uint8_t);

Appelez cette fonction depuis C

byteOutput= byte_mirror(byteInput);

Ceci est le code, il ne convient que pour le noyau 8051. Dans le registre CPU r0 se trouvent les données de byteInput . Code tournez à droite r0 cross carry puis tournez à gauche jusqu'à r1 . Répétez cette procédure 8 fois, pour chaque bit. Ensuite, le registre r1 est renvoyé à la fonction c en tant que byteOutput. Dans 8051 le noyau est seulement possible de faire tourner un accumulateur a .

NAME     BYTE_MIRROR
RSEG     RCODE
PUBLIC   byte_mirror              //8051 core        

byte_mirror
    mov r3,#8;
loop:   
    mov a,r0;
    rrc a;
    mov r0,a;    
    mov a,r1;
    rlc a;   
    mov r1,a;
    djnz r3,loop
    mov r0,a
    ret

AVANTAGES: c'est un faible encombrement, c'est une vitesse élevée CONS: ce n'est pas du code réutilisable, c'est seulement pour 8051

011101101-> porter

101101110 <-porter


Bien que ce code puisse répondre à la question, il serait préférable d'inclure un contexte, en expliquant comment il fonctionne et quand l'utiliser. Les réponses basées uniquement sur le code ne sont pas utiles à long terme.
fNek

0
  xor ax,ax
  xor bx,bx
  mov cx,8
  mov al,original_byte!
cycle:   shr al,1
  jnc not_inc
  inc bl
not_inc: test cx,cx
  jz,end_cycle
  shl bl,1
  loop cycle
end_cycle:

l'octet inversé sera au registre bl


3
Dans un autre contexte qui peut être une réponse juste mais la question portait sur C ou C ++, pas asm ...
jadsq

0
typedef struct
{
    uint8_t b0:1;
    uint8_t b1:1;
    uint8_t b2:1;
    uint8_t b3:1;
    uint8_t b4:1;
    uint8_t b5:1;
    uint8_t b6:1;
    uint8_t b7:1;
} bits_t;

uint8_t reverse_bits(uint8_t src)
{
    uint8_t dst = 0x0;
    bits_t *src_bits = (bits_t *)&src;
    bits_t *dst_bits = (bits_t *)&dst;

    dst_bits->b0 = src_bits->b7;
    dst_bits->b1 = src_bits->b6;
    dst_bits->b2 = src_bits->b5;
    dst_bits->b3 = src_bits->b4;
    dst_bits->b4 = src_bits->b3;
    dst_bits->b5 = src_bits->b2;
    dst_bits->b6 = src_bits->b1;
    dst_bits->b7 = src_bits->b0;

    return dst;
}

Comme note stylistique, je trouve l'utilisation de uint8_tpour les champs 1 bit un peu moche, car il semble d'abord dire que cela prendra 8 bits, mais à la fin de la ligne, le définit comme un seul bit. J'utiliserais unsigned b0:1etc.
Arkku

0
#include <stdio.h>
#include <stdlib.h>

int main()
{
    int i;
    unsigned char rev = 0x70 ; // 0b01110000
    unsigned char tmp = 0;

    for(i=0;i<8;i++)
    {
    tmp |= ( ((rev & (1<<i))?1:0) << (7-i));
    }
    rev = tmp;

    printf("%x", rev);       //0b00001110 binary value of given number
    return 0;
}

Veuillez ajouter quelques explications.
zcui93

0

Je pense que c'est assez simple

uint8_t reverse(uint8_t a)
{
  unsigned w = ((a << 7) & 0x0880) | ((a << 5) & 0x0440) | ((a << 3) & 0x0220) | ((a << 1) & 0x0110);
  return static_cast<uint8_t>(w | (w>>8));
}

ou

uint8_t reverse(uint8_t a)
{
  unsigned w = ((a & 0x11) << 7) | ((a & 0x22) << 5) | ((a & 0x44) << 3) | ((a & 0x88) << 1);
  return static_cast<uint8_t>(w | (w>>8));
}

0
unsigned char c ; // the original
unsigned char u = // the reversed
c>>7&0b00000001 |
c<<7&0b10000000 |
c>>5&0b00000010 |
c<<5&0b01000000 |
c>>3&0b00000100 |
c<<3&0b00100000 |
c>>1&0b00001000 |
c<<1&0b00010000 ;

Explanation: exchanged bits as per the arrows below.
01234567
<------>
#<---->#
##<-->##
###<>###

0

Je vais ajouter ma solution, car je ne trouve rien de tel dans les réponses jusqu'à présent. C'est peut-être un peu sur-conçu, mais il génère la table de recherche en utilisant C ++ 14 std::index_sequenceau moment de la compilation.

#include <array>
#include <utility>

constexpr unsigned long reverse(uint8_t value) {
    uint8_t result = 0;
    for (std::size_t i = 0, j = 7; i < 8; ++i, --j) {
        result |= ((value & (1 << j)) >> j) << i;
    }
    return result;
}

template<size_t... I>
constexpr auto make_lookup_table(std::index_sequence<I...>)
{
    return std::array<uint8_t, sizeof...(I)>{reverse(I)...};   
}

template<typename Indices = std::make_index_sequence<256>>
constexpr auto bit_reverse_lookup_table()
{
    return make_lookup_table(Indices{});
}

constexpr auto lookup = bit_reverse_lookup_table();

int main(int argc)
{
    return lookup[argc];
}

https://godbolt.org/z/cSuWhF


0

Voici une solution simple et lisible, portable sur toutes les plateformes conformes, y compris celles avec sizeof(char) == sizeof(int):

#include <limits.h>

unsigned char reverse(unsigned char c) {
    int shift;
    unsigned char result = 0;

    for (shift = 0; shift < CHAR_BIT; shift++) {
        result <<= 1;
        result |= c & 1;
        c >>= 1;
    }
    return result;
}

0

Je sais que cette question est datée mais je pense toujours que le sujet est pertinent à certaines fins, et voici une version qui fonctionne très bien et qui est lisible. Je ne peux pas dire que ce soit le plus rapide ou le plus efficace, mais il devrait être l'un des plus propres. J'ai également inclus une fonction d'assistance pour afficher facilement les modèles de bits. Cette fonction utilise certaines des fonctions standard de la bibliothèque au lieu d'écrire votre propre manipulateur de bits.

#include <algorithm>
#include <bitset>
#include <exception>
#include <iostream>
#include <limits>
#include <string>

// helper lambda function template
template<typename T>
auto getBits = [](T value) {
    return std::bitset<sizeof(T) * CHAR_BIT>{value};
};

// Function template to flip the bits
// This will work on integral types such as int, unsigned int,
// std::uint8_t, 16_t etc. I did not test this with floating
// point types. I chose to use the `bitset` here to convert
// from T to string as I find it easier to use than some of the
// string to type or type to string conversion functions,
// especially when the bitset has a function to return a string. 
template<typename T>
T reverseBits(T& value) {
    static constexpr std::uint16_t bit_count = sizeof(T) * CHAR_BIT;

    // Do not use the helper function in this function!
    auto bits = std::bitset<bit_count>{value};
    auto str = bits.to_string();
    std::reverse(str.begin(), str.end());
    bits = std::bitset<bit_count>(str);
    return static_cast<T>( bits.to_ullong() );
}

// main program
int main() {
    try {
        std::uint8_t value = 0xE0; // 1110 0000;
        std::cout << +value << '\n'; // don't forget to promote unsigned char
        // Here is where I use the helper function to display the bit pattern
        auto bits = getBits<std::uint8_t>(value);
        std::cout << bits.to_string() << '\n';

        value = reverseBits(value);
        std::cout << +value << '\n'; // + for integer promotion

        // using helper function again...
        bits = getBits<std::uint8_t>(value);
        std::cout << bits.to_string() << '\n';

    } catch(const std::exception& e) {  
        std::cerr << e.what();
        return EXIT_FAILURE;
    }
    return EXIT_SUCCESS;
}

Et cela donne la sortie suivante.

224
11100000
7
00000111

0

Celui-ci m'a aidé avec un ensemble de tableaux matriciels 8x8.

uint8_t mirror_bits(uint8_t var)
{
    uint8_t temp = 0;
    if ((var & 0x01))temp |= 0x80;
    if ((var & 0x02))temp |= 0x40;
    if ((var & 0x04))temp |= 0x20;
    if ((var & 0x08))temp |= 0x10;

    if ((var & 0x10))temp |= 0x08;
    if ((var & 0x20))temp |= 0x04;
    if ((var & 0x40))temp |= 0x02;
    if ((var & 0x80))temp |= 0x01;

    return temp;
}

1
Cette fonction ne fonctionne pas réellement, l'inverse de 0b11001111 devrait être 0b11110011, mais échoue avec cette fonction. La même méthode de test fonctionne pour de nombreuses autres fonctions répertoriées ici.
Dan le

Oui, merci j'ai corrigé ma réponse. Merci de m'avoir fait part de mon erreur :)
R1S8K
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.