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" mise en œuvre par un développeur. Et par inversion je veux dire:
1110 -> 0111
0010 -> 0100
Ceci est similaire à, mais pas un doublon de this PHP question.
Ceci est similaire à, mais pas un duplicata de this C question. Cette question demande la méthode la plus simple à implémenter par un développeur. Le "meilleur algorithme" concerne les performances de la mémoire et du processeur.
Si vous parlez d'un seul octet, une consultation de table est probablement le meilleur choix, à moins que, pour une raison quelconque, vous ne disposiez pas de 256 octets.
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;
}
D'abord, les quatre bits de gauche sont échangés avec les quatre bits de droite. Ensuite, toutes les paires adjacentes sont échangées, puis tous les bits simples adjacents. Cela entraîne un ordre inversé.
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 s'adapte facilement sur une ligne de cache.
Voir le bit bidouiller les hacks pour de nombreuses solutions. Le copypage à 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 “simple à implémenter” signifie quelque chose qui peut être fait sans référence dans un examen ou un entretien d'embauche, le pari le plus sûr est probablement la copie inefficace des bits un par un dans une autre variable dans l'ordre inverse ).
Puisque personne n'a posté 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];
}
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;
}
MODIFIER:
Converti en modèle avec bitcount optionnel
Deux lignes:
for(i=0;i<8;i++)
reversed |= ((original>>i) & 0b1)<<(7-i);
ou si vous avez des problèmes avec la partie "0b1":
for(i=0;i<8;i++)
reversed |= ((original>>i) & 1)<<(7-i);
"original" est l'octet que vous voulez inverser . "inversé" est le résultat initialisé à 0.
Bien que probablement pas portable, j'utiliserais le langage d'assemblage.
De nombreuses langues d'assemblage ont pour instruction de faire pivoter un bit dans le drapeau de report et de le faire pivoter 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 utilisé est beaucoup plus compliqué, car C et C++ ne prennent pas en charge la rotation pour porter et la rotation pour porter. Le drapeau de transport doit être modelé.
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.
La méthode simplest 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;
}
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 entre chaque bit dans 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
Pour constant, 8-bit input, cela ne coûte pas de mémoire, ni d’UC 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))
Je l'ai utilisé 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. Par exemple:
#define LABEL_HF_COMM MSB2LSB(0205)
Vous pouvez être intéressé par std::vector<bool>
(c'est-à-dire en paquets) et std::bitset
Ce devrait être le plus simple comme 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 en 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 une bitset
ou un std::vector<bool>
(ou un boost::dynamic_bitset
) est que vous n'êtes pas limité à des octets ou des mots mais que vous pouvez inverser un nombre arbitraire de bits.
HTH
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;
}
modifier
Regardez ici pour d'autres solutions qui pourraient mieux fonctionner pour vous
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;
}
Cette fonction simple utilise un masque pour tester chaque bit de l'octet d'entrée et le transférer dans une sortie à décalage:
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;
}
Avant d'implémenter une solution algorithmique, vérifiez le langage d'assemblage pour l'architecture de processeur utilisée. Votre architecture peut inclure des instructions qui gèrent de telles manipulations au niveau des bits (et quoi de plus simple qu’une instruction Assembly?).
Si une telle instruction n'est pas disponible, alors je suggérerais d'utiliser 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 lequel des algorithmes d'inversion de bits décrits ici (au prix du stockage de la table de recherche quelque part).
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);
Se débarrasser de l'agitation d'utiliser une boucle for! mais les experts s'il vous plaît dites-moi si cela est efficace et plus rapide?
Celui-ci est basé sur celui 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 et ne nécessite donc pas de ressources supplémentaires.
cela peut aussi ê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 )
#include <stdio.h>
#include <stdlib.h>
#define BIT0 (0x01)
#define BIT1 (0x02)
#define BIT2 (0x04)
#define BIT3 (0x08)
#define BIT4 (0x10)
#define BIT5 (0x20)
#define BIT6 (0x40)
#define BIT7 (0x80)
#define BYTE_TO_BINARY_PATTERN "%c%c%c%c%c%c%c%c\n"
#define BITETOBINARY(byte) \
(byte & BIT7 ? '1' : '0'), \
(byte & BIT6 ? '1' : '0'), \
(byte & BIT5 ? '1' : '0'), \
(byte & BIT4 ? '1' : '0'), \
(byte & BIT3 ? '1' : '0'), \
(byte & BIT2 ? '1' : '0'), \
(byte & BIT1 ? '1' : '0'), \
(byte & BIT0 ? '1' : '0') \
#define BITETOBINARYREVERSE(byte) \
(byte & BIT0 ? '1' : '0'), \
(byte & BIT1 ? '1' : '0'), \
(byte & BIT2 ? '1' : '0'), \
(byte & BIT3 ? '1' : '0'), \
(byte & BIT4 ? '1' : '0'), \
(byte & BIT5 ? '1' : '0'), \
(byte & BIT6 ? '1' : '0'), \
(byte & BIT7 ? '1' : '0') \
int main()
{
int i,j,c;
i |= BIT2|BIT7;
printf("0x%02X\n",i);
printf(BYTE_TO_BINARY_PATTERN,BITETOBINARY(i));
printf("Reverse");
printf(BYTE_TO_BINARY_PATTERN,BITETOBINARYREVERSE(i));
return 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));
}
Je vais intégrer ma solution, car je n'ai rien trouvé de tel dans les réponses jusqu'à présent. Il est peut-être un peu trop pensé, mais il génère la table de recherche en utilisant C++ 14 std::index_sequence
au 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];
}
Voici une solution simple et lisible, portable pour toutes les plates-formes 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;
}
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
<------>
#<---->#
##<-->##
###<>###
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;
}
#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;
}