Embora existam várias maneiras de inverter a ordem dos bits em um byte, estou curioso para saber qual é a "mais simples" para um desenvolvedor implementar. E ao inverter, quero dizer:
1110 -> 0111
0010 -> 0100
Isso é semelhante, mas não uma duplicata deste questão do PHP.
Isso é semelhante, mas não uma duplicata, desta questão C. Esta questão está pedindo o método mais fácil de implementar por um desenvolvedor. O "Melhor Algoritmo" se preocupa com a memória e o desempenho da CPU.
c++
c
bit-manipulation
Nathan
fonte
fonte
Respostas:
Se você está falando sobre um único byte, uma consulta de tabela é provavelmente a melhor aposta, a menos que por algum motivo você não tenha 256 bytes disponíveis.
fonte
unsigned int rtable[] = {0x800, 0x4000, ...};
. Então jogue fora o roteiro e esqueça que você o teve. É muito mais rápido de escrever do que o código C ++ equivalente e só será executado uma vez, portanto, você obtém o tempo de execução O (1) em seu código C ++.Isso deve funcionar:
Primeiro, os quatro bits da esquerda são trocados pelos quatro bits da direita. Em seguida, todos os pares adjacentes são trocados e, em seguida, todos os bits únicos adjacentes. Isso resulta em uma ordem reversa.
fonte
Acho que uma tabela de consulta deve ser um dos métodos mais simples. No entanto, você não precisa de uma tabela de pesquisa completa.
É bastante simples de codificar e verificar visualmente.
No final das contas, isso pode até ser mais rápido do que uma mesa cheia. O bit arith é barato e a tabela cabe facilmente em uma linha de cache.
fonte
b0001(1) -> b1000(0x8)
,b0010(2) -> b0100(0x4)
,b1010(10) -> b0101(0x5)
. Veja o padrão? É simples o suficiente para que você possa calculá-lo em sua cabeça (se você pode ler binário, caso contrário, você precisará de papel para resolvê-lo). Quanto ao salto, reverter um número inteiro de 8 bits é o mesmo que reverter partes de 4 bits e depois trocá-las; Eu reivindico experiência e intuição (ou magia).Veja os pequenos truques de hacks para muitas soluções. Copiar de lá é obviamente simples de implementar. =)
Por exemplo (em uma CPU de 32 bits):
Se por "simples de implementar" se entende algo que pode ser feito sem uma referência em um exame ou entrevista de emprego, então a aposta mais segura é provavelmente a cópia ineficiente de bits um a um em outra variável na ordem inversa (já mostrado em outras respostas )
fonte
Como ninguém postou uma solução completa de pesquisa de tabela, aqui está a minha:
fonte
EDITAR:
Convertido em um modelo com o bitcount opcional
fonte
sizeof(T)*8
porsizeof(T)*CHAR_BITS
.sizeof(T)*CHAR_BIT
porstd::numeric_limits<T>::digits
(quase 4 anos de pedantismo depois).CHAR_BIT
, nãoCHAR_BITS
.Duas linhas:
ou caso você tenha problemas com a parte "0b1":
"original" é o byte que você deseja reverter. "reverso" é o resultado, inicializado com 0.
fonte
Embora provavelmente não seja portátil, eu usaria a linguagem assembly.
Muitas linguagens assembly têm instruções para girar um pouco na bandeira de carry e girar a bandeira de carry para a palavra (ou byte).
O algoritmo é:
O código de linguagem de alto nível para isso é muito mais complicado, porque C e C ++ não suportam rotação para transportar e rotação para transporte. A bandeira de transporte deve ser modelada.
Editar: linguagem de montagem, por exemplo
fonte
Acho a solução a seguir mais simples do que os outros algoritmos de manipulação de bits que vi aqui.
Ele obtém cada bit do byte e o desloca de acordo, começando do primeiro ao último.
Explicação:
fonte
A maneira mais simples é provavelmente iterar sobre as posições dos bits em um loop:
fonte
CHAR_BIT
quando você assumechar
que tem 8 bits?Você pode estar interessado em
std::vector<bool>
(que é um pacote de bits) estd::bitset
Deve ser o mais simples conforme solicitado.
Outras opções podem ser mais rápidas.
EDIT: Devo-lhe uma solução usando
std::vector<bool>
O segundo exemplo requer a extensão c ++ 0x (para inicializar a matriz com
{...}
). A vantagem de usar abitset
ou astd::vector<bool>
(ou aboost::dynamic_bitset
) é que você não está limitado a bytes ou palavras, mas pode reverter um número arbitrário de bits.HTH
fonte
std::vector<bool> b = { ... }; std::vector<bool> rb ( b.rbegin(), b.rend());
- usando iteradores reversos diretamente?Para o caso muito limitado de entrada constante de 8 bits , este método não custa memória ou CPU em tempo de execução:
Usei isso para ARINC-429, em que a ordem dos bits (endianidade) do rótulo é oposta ao resto da palavra. O rótulo costuma ser uma constante e, convencionalmente, em octal.
É assim que eu usei para definir uma constante, porque a especificação define esse rótulo como big-endian 205 octal.
Mais exemplos:
fonte
Existem muitas maneiras de reverter bits, dependendo do que você quer dizer, a "maneira mais simples".
Reverter por rotação
Provavelmente o mais lógico, consiste em girar o byte enquanto aplica uma máscara no primeiro bit
(n & 1)
:1) Como o comprimento de um unsigner char é de 1 byte, que é igual a 8 bits, isso significa que iremos escanear cada bit
while (byte_len--)
2) Primeiro verificamos se b está um pouco na extrema direita com
(b & 1)
; se for assim, definimos o bit 1 em r com|
e o movemos apenas 1 bit para a esquerda multiplicando r por 2 com(r << 1)
3) Em seguida, dividimos nosso unsigned char b por 2 com
b >>=1
para apagar o bit localizado na extrema direita da variável b. Como um lembrete, b >> = 1; é equivalente a b / = 2;Reverse em uma linha
Esta solução é atribuída a Rich Schroeppel na seção Programming Hacks
1) A operação de multiplicação (b * 0x0202020202ULL) cria cinco cópias separadas do padrão de bytes de 8 bits para se espalhar em um valor de 64 bits.
2) A operação AND (& 0x010884422010ULL) seleciona os bits que estão nas posições corretas (invertidas), em relação a cada grupo de bits de 10 bits.
3) Juntas, as operações de multiplicação e AND copiam os bits do byte original de forma que cada um apareça em apenas um dos conjuntos de 10 bits. As posições invertidas dos bits do byte original coincidem com suas posições relativas em qualquer conjunto de 10 bits.
4) A última etapa (% 0x3ff), que envolve a divisão do módulo por 2 ^ 10-1 tem o efeito de mesclar cada conjunto de 10 bits (das posições 0-9, 10-19, 20-29, ...) no valor de 64 bits. Eles não se sobrepõem, portanto, as etapas de adição subjacentes à divisão do módulo se comportam como operações OR.
Solução para dividir e conquistar
Esta é a resposta mais votada e apesar de algumas explicações, acho que para a maioria das pessoas parece difícil visualizar o que 0xF0, 0xCC, 0xAA, 0x0F, 0x33 e 0x55 realmente significam.
Ele não tira proveito de '0b', que é uma extensão do GCC e está incluído desde o padrão C ++ 14, lançado em dezembro de 2014, portanto, algum tempo depois desta resposta datada de abril de 2010
Verifique os trechos de código abaixo para lembrar e compreender ainda melhor esta solução em que avançamos meio a meio:
NB: O
>> 4
é porque existem 8 bits em 1 byte, que é um caractere unsigned, então queremos pegar a outra metade, e assim por diante.Poderíamos facilmente aplicar esta solução a 4 bytes com apenas duas linhas adicionais e seguindo a mesma lógica. Como as duas máscaras se complementam, podemos até usar ~ para trocar bits e economizar tinta:
[Somente C ++] Reverter qualquer não assinado (modelo)
A lógica acima pode ser resumida com um loop que funcionaria em qualquer tipo de sem sinal:
Experimente você mesmo com a inclusão da função acima:
Reverter com asm volátil
Por último, mas não menos importante, se mais simples significa menos linhas, por que não experimentar a montagem embutida?
Você pode testar o snippet de código abaixo adicionando
-masm=intel
durante a compilação:Explicações linha por linha:
fonte
Consulta de tabela ou
editar
Procure aqui outras soluções que podem funcionar melhor para você
fonte
uma implementação mais lenta, porém mais simples:
fonte
Essa pode ser uma solução rápida?
Livre-se da confusão de usar um loop for! mas os especialistas, por favor, me digam se isso é eficiente e rápido?
fonte
Antes de implementar qualquer solução algorítmica, verifique a linguagem assembly para qualquer arquitetura de CPU que você está usando. Sua arquitetura pode incluir instruções que tratam de manipulações bit a bit como esta (e o que poderia ser mais simples do que uma única instrução de montagem?).
Se tal instrução não estiver disponível, então eu sugeriria ir com a rota da tabela de pesquisa. Você pode escrever um script / programa para gerar a tabela para você, e as operações de pesquisa seriam mais rápidas do que qualquer um dos algoritmos de reversão de bits aqui (ao custo de ter que armazenar a tabela de pesquisa em algum lugar).
fonte
Esta função simples usa uma máscara para testar cada bit no byte de entrada e transferi-lo para uma saída de deslocamento:
fonte
Este é baseado em um BobStein-VisiBone fornecido
Eu realmente gosto muito deste porque o compilador automaticamente lida com o trabalho para você, portanto, não requer mais recursos.
isso também pode ser estendido para 16 bits ...
fonte
b
entre parênteses no caso de ser uma expressão mais complexa do que um único número, e talvez também renomeie a macroREVERSE_BYTE
como uma dica de que você provavelmente não deseja ter uma expressão mais complexa (tempo de execução) lá. Ou torne-a uma função embutida. (Mas no geral eu gosto disso por ser simples o suficiente para que você possa fazer isso facilmente de memória com muito pouca chance de erro.)Supondo que seu compilador permite não assinado por muito tempo :
Descoberto aqui
fonte
Se você usa um microcontrolador pequeno e precisa de uma solução de alta velocidade com pegada pequena, esta pode ser uma solução. É possível usá-lo para projeto C, mas você precisa adicionar este arquivo como um arquivo assembler * .asm, ao seu projeto C. Instruções: No projeto C, adicione esta declaração:
Chame esta função de C
Este é o código, só é adequado para 8051 core. No registro da CPU, r0 são dados de byteInput . O código gira para a direita r0 cross carry e, em seguida, gire o carry para a esquerda para r1 . Repita este procedimento 8 vezes, para cada bit. Em seguida, o registrador r1 é retornado para a função c como byteOutput. Em 8051 o núcleo só é possível girar o acumulador a .
PRÓS: é pequeno, é de alta velocidade. CONTRAS: Não é um código reutilizável, é apenas para 8051
011101101-> carry
101101110 <-carry
fonte
o byte invertido estará no registro bl
fonte
fonte
uint8_t
para os campos de 1 bit um pouco feio, já que parece primeiro dizer que levará 8 bits, mas no final da linha o define como apenas um único bit. Eu usariaunsigned b0:1
etc.fonte
Eu acho que isso é bastante simples
ou
fonte
fonte
Vou adicionar minha solução, já que não consigo encontrar nada parecido com isso nas respostas até agora. Talvez seja um pouco sobrecarregado, mas gera a tabela de pesquisa usando C ++ 14
std::index_sequence
em tempo de compilação.https://godbolt.org/z/cSuWhF
fonte
Aqui está uma solução simples e legível, portátil para todas as plataformas em conformidade, incluindo aquelas com
sizeof(char) == sizeof(int)
:fonte
Sei que esta pergunta está desatualizada, mas ainda acho que o assunto é relevante para alguns fins, e aqui está uma versão que funciona muito bem e é legível. Não posso dizer que seja o mais rápido ou o mais eficiente, mas deve ser um dos mais limpos. Também incluí uma função auxiliar para exibir facilmente os padrões de bits. Esta função usa algumas das funções de biblioteca padrão em vez de escrever seu próprio manipulador de bits.
E dá a seguinte saída.
fonte
Este me ajudou com o conjunto de matrizes de 8x8 pontos.
fonte