Si bien hay varias formas de invertir el orden de los bits en un byte, tengo curiosidad por saber cuál es la "más simple" de implementar para un desarrollador. Y al revertir quiero decir:
1110 -> 0111
0010 -> 0100
Esto es similar, pero no un duplicado de esta pregunta de PHP.
Esto es similar, pero no un duplicado de esta pregunta C. Esta pregunta solicita el método más fácil de implementar por parte de un desarrollador. El "Mejor algoritmo" se ocupa de la memoria y el rendimiento de la CPU.
c++
c
bit-manipulation
nathan
fuente
fuente
Respuestas:
Si está hablando de un solo byte, una búsqueda de tabla es probablemente la mejor opción, a menos que por alguna razón no tenga 256 bytes disponibles.
fuente
unsigned int rtable[] = {0x800, 0x4000, ...};
. Luego, deseche el guión y olvídese de que lo tuvo. Es mucho más rápido de escribir que el código C ++ equivalente, y solo se ejecutará una vez, por lo que obtienes tiempo de ejecución O (1) en tu código C ++.Esto debería funcionar:
Primero, los cuatro bits de la izquierda se intercambian con los cuatro bits de la derecha. Luego, se intercambian todos los pares adyacentes y luego todos los bits individuales adyacentes. Esto da como resultado un orden inverso.
fuente
Creo que una tabla de búsqueda tiene que ser uno de los métodos más simples. Sin embargo, no necesita una tabla de búsqueda completa.
Esto es bastante simple de codificar y verificar visualmente.
En última instancia, esto podría ser incluso más rápido que una mesa completa. El arit de bits es barato y la tabla cabe fácilmente en una línea de caché.
fuente
b0001(1) -> b1000(0x8)
,b0010(2) -> b0100(0x4)
,b1010(10) -> b0101(0x5)
. ¿Ves el patrón? Es lo suficientemente simple como para que pueda calcularlo en su cabeza (si puede leer binario, de lo contrario necesitará papel para resolverlo). En cuanto al salto, invertir un entero de 8 bits es lo mismo que invertir partes de 4 bits y luego intercambiarlas; Reclamo experiencia e intuición (o magia).Vea los trucos de juguete para muchas soluciones. Copiar desde allí es obviamente fácil de implementar. =)
Por ejemplo (en una CPU de 32 bits):
Si por "simple de implementar" uno significa algo que se puede hacer sin una referencia en un examen o entrevista de trabajo, entonces la apuesta más segura es probablemente la copia ineficiente de bits uno por uno en otra variable en orden inverso (ya se muestra en otras respuestas ).
fuente
Como nadie publicó una solución completa de búsqueda de tablas, aquí está la mía:
fuente
EDITAR:
Convertido en una plantilla con el bitcount opcional
fuente
sizeof(T)*8
consizeof(T)*CHAR_BITS
.sizeof(T)*CHAR_BIT
porstd::numeric_limits<T>::digits
(casi 4 años de pedantería más tarde).CHAR_BIT
, noCHAR_BITS
.Dos lineas:
o en caso de que tenga problemas con la parte "0b1":
"original" es el byte que desea invertir. "invertido" es el resultado, inicializado a 0.
fuente
Aunque probablemente no sea portátil, usaría lenguaje ensamblador.
Muchos lenguajes ensambladores tienen instrucciones para rotar un poco en la bandera de acarreo y para rotar la bandera de acarreo en la palabra (o byte).
El algoritmo es:
El código de lenguaje de alto nivel para esto es mucho más complicado, porque C y C ++ no admiten rotar para llevar y rotar desde llevar. La bandera de acarreo tiene que modelarse.
Editar: lenguaje ensamblador, por ejemplo
fuente
Encuentro la siguiente solución más simple que los otros algoritmos de manipulación de bits que he visto aquí.
Obtiene cada bit del byte y lo desplaza en consecuencia, comenzando desde el primero hasta el último.
Explicación:
fuente
La forma más sencilla es probablemente iterar sobre las posiciones de los bits en un bucle:
fuente
CHAR_BIT
cuando asumechar
que tiene 8 bits?Para el caso muy limitado de entrada constante de 8 bits , este método no cuesta memoria ni CPU en tiempo de ejecución:
Usé esto para ARINC-429 donde el orden de bits (endianidad) de la etiqueta es opuesto al resto de la palabra. La etiqueta es a menudo una constante y convencionalmente en octal.
Así es como lo usé para definir una constante, porque la especificación define esta etiqueta como big-endian 205 octal.
Más ejemplos:
fuente
Puede estar interesado en
std::vector<bool>
(que está empaquetado en bits) ystd::bitset
Debe ser el más simple que se solicite.
Otras opciones pueden ser más rápidas.
EDITAR: te debo una solución usando
std::vector<bool>
El segundo ejemplo requiere la extensión c ++ 0x (para inicializar la matriz
{...}
). La ventaja de usar abitset
o astd::vector<bool>
(o aboost::dynamic_bitset
) es que no está limitado a bytes o palabras, sino que puede invertir un número arbitrario de bits.HTH
fuente
std::vector<bool> b = { ... }; std::vector<bool> rb ( b.rbegin(), b.rend());
utilizamos iteradores inversos directamente?Hay muchas formas de invertir bits dependiendo de lo que quiera decir con la "forma más sencilla".
Invertir por rotación
Probablemente el más lógico, consiste en rotar el byte aplicando una máscara en el primer bit
(n & 1)
:1) Como la longitud de un carácter sin firma es de 1 byte, que es igual a 8 bits, significa que escanearemos cada bit
while (byte_len--)
2) Primero verificamos si b está un poco en el extremo derecho con
(b & 1)
; si es así, establecemos el bit 1 en r con|
y lo movemos solo 1 bit a la izquierda multiplicando r por 2 con(r << 1)
3) Luego dividimos nuestro carácter b sin signo por 2 con
b >>=1
para borrar el bit ubicado en el extremo derecho de la variable b. Como recordatorio, b >> = 1; es equivalente a b / = 2;Reverso en una línea
Esta solución se atribuye a Rich Schroeppel en la sección Programming Hacks
1) La operación de multiplicación (b * 0x0202020202ULL) crea cinco copias separadas del patrón de bytes de 8 bits para distribuirlas en un valor de 64 bits.
2) La operación AND (& 0x010884422010ULL) selecciona los bits que están en las posiciones correctas (invertidas), en relación con cada grupo de bits de 10 bits.
3) Juntas, las operaciones de multiplicación y Y copian los bits del byte original para que aparezcan en solo uno de los conjuntos de 10 bits. Las posiciones invertidas de los bits del byte original coinciden con sus posiciones relativas dentro de cualquier conjunto de 10 bits.
4) El último paso (% 0x3ff), que implica la división del módulo por 2 ^ 10 - 1, tiene el efecto de fusionar cada conjunto de 10 bits (desde las posiciones 0-9, 10-19, 20-29, ...) en el valor de 64 bits. No se superponen, por lo que los pasos de suma subyacentes a la división del módulo se comportan como operaciones OR.
Solución de dividir y conquistar
Esta es la respuesta más votada y, a pesar de algunas explicaciones, creo que para la mayoría de las personas se siente difícil visualizar lo que realmente significan 0xF0, 0xCC, 0xAA, 0x0F, 0x33 y 0x55.
No aprovecha '0b', que es una extensión de GCC y se incluye desde el estándar C ++ 14, lanzado en diciembre de 2014, así que un tiempo después de esta respuesta que data de abril de 2010
Consulte los siguientes fragmentos de código para recordar y comprender aún mejor esta solución en la que nos movemos mitad a mitad:
NB: Esto
>> 4
se debe a que hay 8 bits en 1 byte, que es un carácter sin signo, por lo que queremos tomar la otra mitad, y así sucesivamente.Podríamos aplicar fácilmente esta solución a 4 bytes con solo dos líneas adicionales y siguiendo la misma lógica. Dado que ambas máscaras se complementan, incluso podemos usar ~ para cambiar bits y ahorrar algo de tinta:
[Solo C ++] Revertir cualquier sin firmar (plantilla)
La lógica anterior se puede resumir con un bucle que funcionaría en cualquier tipo de sin firmar:
Pruébelo usted mismo con la inclusión de la función anterior:
Invertir con asm volátil
Por último, pero no menos importante, si lo más simple significa menos líneas, ¿por qué no intentar el ensamblaje en línea?
Puede probar el siguiente fragmento de código agregando
-masm=intel
al compilar:Explicaciones línea por línea:
fuente
Búsqueda de tabla o
editar
Busque aquí otras soluciones que podrían funcionar mejor para usted
fuente
una implementación más lenta pero más simple:
fuente
¿Puede ser esta una solución rápida?
¡Se deshace del ajetreo de usar un bucle for! pero expertos, por favor, díganme si esto es eficiente y rápido.
fuente
Antes de implementar cualquier solución algorítmica, verifique el lenguaje ensamblador para cualquier arquitectura de CPU que esté utilizando. Su arquitectura puede incluir instrucciones que manejan manipulaciones bit a bit como esta (¿y qué podría ser más simple que una sola instrucción de ensamblaje?).
Si tal instrucción no está disponible, sugeriría seguir la ruta de la tabla de búsqueda. Puede escribir un script / programa para generar la tabla por usted, y las operaciones de búsqueda serían más rápidas que cualquiera de los algoritmos de inversión de bits aquí (a costa de tener que almacenar la tabla de búsqueda en algún lugar).
fuente
Esta función simple usa una máscara para probar cada bit en el byte de entrada y transferirlo a una salida cambiante:
fuente
Éste se basa en el proporcionado por BobStein-VisiBone
Realmente me gusta mucho este porque el compilador maneja automáticamente el trabajo por usted, por lo que no requiere más recursos.
esto también se puede extender a 16 bits ...
fuente
b
entre paréntesis en caso de que sea una expresión más compleja que un solo número, y tal vez también cambie el nombre de la macroREVERSE_BYTE
como una pista de que probablemente no desee tener una expresión más compleja (tiempo de ejecución) allí. O conviértalo en una función en línea. (Pero en general, me gusta esto por ser lo suficientemente simple como para que pueda hacerlo desde la memoria fácilmente con muy pocas posibilidades de error).Suponiendo que su compilador permita unsigned long long :
Descubierto aquí
fuente
Si utiliza un microcontrolador pequeño y necesita una solución de alta velocidad con un tamaño reducido, esto podría ser una solución. Es posible usarlo para un proyecto C, pero necesita agregar este archivo como archivo ensamblador * .asm, a su proyecto C. Instrucciones: En el proyecto C, agregue esta declaración:
Llame a esta función desde C
Este es el código, solo es adecuado para 8051 core. En el registro de la CPU, r0 hay datos de byteInput . El código gire a la derecha r0 cross carry y luego gire a la izquierda a r1 . Repita este procedimiento 8 veces, por cada bit. Luego, el registro r1 se devuelve a la función c como byteOutput. En el núcleo 8051 solo es posible rotar el acumulador a .
PROS: Ocupa poco espacio, es de alta velocidad CONTRAS: No es código reutilizable, es solo para 8051
011101101-> llevar
101101110 <-cargar
fuente
el byte invertido estará en el registro bl
fuente
fuente
uint8_t
para los campos de 1 bit un poco feo, ya que parece decir primero que tomará 8 bits pero luego al final de la línea lo define como un solo bit. Me gustaría utilizarunsigned b0:1
etcfuente
Creo que esto es bastante simple
o
fuente
fuente
Incluiré mi solución, ya que hasta ahora no puedo encontrar nada como esto en las respuestas. Quizás esté un poco sobre-diseñado, pero genera la tabla de búsqueda usando C ++ 14
std::index_sequence
en tiempo de compilación.https://godbolt.org/z/cSuWhF
fuente
Aquí hay una solución simple y legible, portátil para todas las plataformas compatibles, incluidas aquellas con
sizeof(char) == sizeof(int)
:fuente
Sé que esta pregunta está fechada, pero sigo pensando que el tema es relevante para algunos propósitos, y aquí hay una versión que funciona muy bien y es legible. No puedo decir que sea el más rápido o el más eficiente, pero debería ser uno de los más limpios. También he incluido una función auxiliar para mostrar fácilmente los patrones de bits. Esta función usa algunas de las funciones de la biblioteca estándar en lugar de escribir su propio manipulador de bits.
Y da el siguiente resultado.
fuente
Este me ayudó con el conjunto de matrices de matriz de puntos de 8x8.
fuente