¿Para qué sirven los operadores de bits? [cerrado]


19

Los lenguajes de programación a menudo vienen con varios operadores de bits (por ejemplo, desplazamiento bit a izquierda y derecha, bit a bit AND, OR, XOR ...). Aunque no se usan mucho, o al menos así ha sido mi experiencia. A veces se usan en desafíos de programación o preguntas de entrevistas, o la solución podría requerirlos, por ejemplo:

  • Sin usar ningún operador de igualdad, cree una función que regrese truecuando dos valores sean iguales
  • Sin usar una tercera variable, intercambie el valor de dos variables

Estos, de nuevo, probablemente tienen pocos usos en el mundo real . Supongo que deberían ser más rápidos porque manipulan directamente la memoria en un nivel bajo.

¿Por qué se encuentran en la mayoría de los lenguajes de programación? ¿Algún caso de uso del mundo real?


@Anto: un ejemplo sencillo sería enviar 256Kb de datos a una velocidad de 256 palabras a la vez (4096 bytes) a un cliente.
Ramhound

1
"Sin utilizar ningún operador de igualdad, cree una función que devuelva verdadero cuando dos valores son iguales" - en C return !(x-y);:? No sé
Andrew Arnold

@ Andrew: Esa es una solución, pero también puedes hacerlo con operadores bit a bit.
Anto

19
"Estos no se acostumbran mucho" - ¿Seguro de eso? Los uso todo el tiempo. No todos trabajamos en su dominio problemático.
Ed S.

2
No es suficiente para obtener una respuesta completa, pero intente leer los 4 bits superiores de un byte sin tocar el bit y luego considere que algunos formatos de datos están muy apretados.
Brendan Long

Respuestas:


53

No, tienen muchas aplicaciones del mundo real y son operaciones fundamentales en las computadoras.

Se utilizan para

  • Hacer malabarismos con bloques de bytes que no encajan en los tipos de datos de los lenguajes de programación
  • Cambio de codificación de ida y vuelta de endian grande a pequeño.
  • Empaque 4 piezas de datos de 6 bits en 3 bytes para alguna conexión en serie o usb
  • Muchos formatos de imagen tienen diferentes cantidades de bits asignados a cada canal de color.
  • Cualquier cosa que involucre pines IO en aplicaciones integradas
  • Compresión de datos, que a menudo no tiene datos que se ajusten a lindos límites de 8 bits.
  • Algoritmos hash, CRC u otras verificaciones de integridad de datos.
  • Cifrado
  • Generación de número de psuedorandom
  • La incursión 5 usa XOR bit a bit entre volúmenes para calcular la paridad.
  • Toneladas más

De hecho, lógicamente, todas las operaciones en una computadora finalmente se reducen a combinaciones de estas operaciones bit a nivel bajo, que tienen lugar dentro de las puertas eléctricas del procesador.


1
+1 para su lista bastante completa, que incluso parece estar agregando
Anto

28
+1. @Anto: esta lista no es ni mucho menos completa. Una lista completa de casos de uso para operadores bit a bit en la programación de sistemas sería tan larga como una lista completa de consultas SQL en aplicaciones comerciales. Dato curioso: uso operaciones bit a bit todo el tiempo, pero no he escrito una declaración SQL en años ;-)
nikie 05 de

44
@nikie: ¡Y escribo SQL todo el tiempo, pero no he usado operadores bit a bit en años! :)
FrustratedWithFormsDesigner

3
Trabajo en sistemas embebidos: los operadores bit a bit son pan y mantequilla. Usado todos los días sin pensar en absoluto.
rapid_now

99
Si ocasionalmente uso el desplazamiento de bits en SQL, ¿obtengo un premio?
Ant

13

Porque son operaciones fundamentales.

Por la misma línea de pensamiento, se podría argumentar que la suma tiene pocos usos en el mundo real, ya que se puede reemplazar por completo por sustracción (y negación) y multiplicación. Pero seguimos sumando porque es una operación fundamental.

Y no piense por un momento que solo porque no haya visto mucha necesidad de operaciones bit a bit no significa que no se usen con mucha frecuencia. De hecho, he usado operaciones bit a bit en casi todos los idiomas que he usado para cosas como el enmascaramiento de bits.

Fuera de mi cabeza, he usado operaciones bit a bit para el procesamiento de imágenes, campos de bits y banderas, procesamiento de texto (por ejemplo, todos los caracteres de una clase en particular a menudo comparten un patrón de bits común), codificación y decodificación de datos serializados, decodificación de VM o CPU códigos de operación, y así sucesivamente. Sin operaciones bit a bit, la mayoría de estas tareas requerirían operaciones muchas veces más complejas para realizar la tarea de manera menos confiable o con una lectura más pobre.

Por ejemplo:

// Given a 30-bit RGB color value as a 32-bit int
// A lot of image sensors spit out 10- or 12-bit data
// and some LVDS panels have a 10- or 12-bit format
b = (color & 0x000003ff);
g = (color & 0x000ffc00) >> 10;
r = (color & 0x3ff00000) >> 20;

// Going the other way:
color = ((r << 20) & 0x3ff00000) | ((g << 10) & 0x000ffc00) | (b & 0x000003ff);

La decodificación de las instrucciones de CPU para CPU de tipo RISC (como cuando se emula otra plataforma) requiere extraer porciones de un valor grande como se indicó anteriormente. A veces, realizar estas operaciones con multiplicación y división y módulo, etc., puede ser hasta diez veces más lento que las operaciones bit a bit equivalentes.


12

Un ejemplo típico es extraer los colores individuales de un valor RGB de 24 bits y viceversa.


EDITAR: de http://www.docjar.com/html/api/java/awt/Color.java.html

    value =  ((int)(frgbvalue[2]*255 + 0.5))    |
                (((int)(frgbvalue[1]*255 + 0.5)) << 8 )  |
                (((int)(frgbvalue[0]*255 + 0.5)) << 16 ) |
                (((int)(falpha*255 + 0.5)) << 24 );

¿Mostrar este ejemplo en la práctica? ¿Un fragmento de código?
Anto

Un mejor ejemplo podría ser el procesamiento de valores RGB de 16 bits (4.5bpc) o 30 bits (10bpc).
greyfade

@grey, siéntase libre de agregar tales ejemplos.

6

Aquí hay un ejemplo del mundo real que encontrarás en Quake 3, Quake 4. Doom III. Todos esos juegos que usaban el motor Q3 .

float Q_rsqrt( float number )
{
        long i;
        float x2, y;
        const float threehalfs = 1.5F;

        x2 = number * 0.5F;
        y  = number;
        i  = * ( long * ) &y;                       // evil floating point bit level hacking [sic]
        i  = 0x5f3759df - ( i >> 1 );               // what the fuck? [sic]
        y  = * ( float * ) &i;
        y  = y * ( threehalfs - ( x2 * y * y ) );   // 1st iteration
//    y  = y * ( threehalfs - ( x2 * y * y ) );   // 2nd iteration, this can be removed

        return y;
}

(Para comprender ese código, debe comprender cómo se almacenan los números de coma flotante, definitivamente no puedo dar más detalles al respecto)

En términos de uso, a menos que se encuentre en campos que requieren cambios de bits, como redes o gráficos, entonces puede encontrar su propósito ligeramente académico. Pero sigue siendo interesante (al menos para mí).


+1 Para esos comentarios, incluso si no son tuyos. Me hizo reír.
Bassinator

4

El cambio es más rápido que multiplicar o dividir por una potencia de dos. Por ejemplo, a << = 2 multiplica a por 4. Por el contrario, a >> = 2 divide a por cuatro. También se pueden enviar datos a un dispositivo utilizando los operadores de bits. Por ejemplo, podemos enviar N flujos de datos en serie desde un puerto N pin mediante las operaciones shift, xor y "y" dentro de N bucles. Cualquier cosa que se pueda lograr en lógica digital también se puede lograr en software y viceversa.


1
Solo tenga cuidado al dividir con redondeo hacia arriba o hacia abajo, etc. El desplazamiento no tiene en cuenta eso, por lo que he descubierto que en realidad es una mejor práctica usar un código de división cuando me refiero a una división y dejar que el compilador lo optimice en un cambio y agregue para mi.
Daemin

@Daemin: estoy trabajando con enteros cuando uso esta técnica. El comportamiento predeterminado para la división de enteros en C y C ++ es el truncamiento hacia cero; por lo tanto, desplazar un número entero a la derecha por una potencia de dos produce el mismo resultado que dividir un número entero por una potencia de dos.
bit-twiddler

1
@ bit-twiddler El desplazamiento a la derecha no funciona de la misma manera que la división para números negativos.
Daemin

@ Daemin: Parece que estás empeñado en demostrarme que estoy equivocado. Primero, vomitas el problema de redondeo. Cuando repudio esa afirmación al afirmar que la división en C y C ++ se trunca hacia cero, descarta el problema del entero con signo. ¿Dónde dije que estaba aplicando el operador shift a los enteros negativos del complemento de dos firmados? Dicho esto, todavía se puede usar el operador de turno para dividir por una potencia de dos. Sin embargo, dado que C y C ++ realizan un desplazamiento a la derecha aritmético en lugar de un desplazamiento a la derecha antiguo, primero se debe verificar si el valor es negativo. Si el valor es negativo,
bit-twiddler

1
Exactamente, tenga cuidado al usar el desplazamiento como un sustituto de la multiplicación y la división, ya que existen diferencias sutiles. Ni mas ni menos.
Daemin

3

Hace mucho, mucho tiempo, los operadores de bits eran útiles. Hoy lo son menos. Oh, no son completamente inútiles, pero ha pasado mucho tiempo desde que vi uno usado que debería haber sido usado.

En 1977 fui programador de lenguaje ensamblador. Estaba convencido de que ensamblador era el único lenguaje verdadero. Estaba seguro de que un lenguaje como Pascal era para pitos académicos que nunca tuvieron que hacer nada real .

Luego leí "El lenguaje de programación C" de Kernighan y Ritchie. Me cambió de opinión por completo. ¿La razón? ¡Tenía operadores poco! Que era un lenguaje ensamblador! Simplemente tenía una sintaxis diferente.

En aquellos días no podía concebir escribir código sin ands, ors, turnos y rotaciones. Hoy en día casi nunca los uso.

Entonces, la respuesta corta a su pregunta es: "Nada". Pero eso no es del todo justo. Entonces la respuesta más larga es: "Principalmente nada".


xkcd.com/378 viene a la mente.
Maxpm

Los operadores de bits son útiles hasta el día de hoy. El hecho de que en su dominio no se usen no hace que no se use o incluso no se use con mucha frecuencia. Aquí hay un ejemplo simple: intente e implemente AES sin operadores de bits. Ese es un ejemplo inesperado de algo que se hace en la mayoría de las computadoras diariamente cientos o miles de veces al día.
SOLO MI OPINIÓN correcta

Codificar / decodificar datos sin usar operadores de bits es doloroso en el mejor de los casos. Por ejemplo, agregar archivos adjuntos MIME a un mensaje requiere que podamos manejar la codificación de datos de tres a cuatro (también conocida como codificación radix64).
bit-twiddler

2

Cifrado

Sugiero echar un vistazo a un fragmento muy pequeño del algoritmo de cifrado DES :

temp = ((left >>> 1) ^ right) & 0x55555555; right ^= temp; left ^= (temp << 1);
temp = ((right >>> 8) ^ left) & 0x00ff00ff; left ^= temp; right ^= (temp << 8);
temp = ((right >>> 2) ^ left) & 0x33333333; left ^= temp; right ^= (temp << 2);
temp = ((left >>> 16) ^ right) & 0x0000ffff; right ^= temp; left ^= (temp << 16);
temp = ((left >>> 4) ^ right) & 0x0f0f0f0f; right ^= temp; left ^= (temp << 4);

Aunque no estoy seguro de que se recomienda DES en estos días: P
Armand

@Alison: No, pero creo que los algoritmos de cifrado que lo han reemplazado implican aún más operaciones de manipulación de bits. :-)
Carson63000

@Alison, por supuesto, pero TripleDES solo se hace DES 3 veces con 3 veces los bits clave.
Scott Whitlock

2

Muchas buenas respuestas, así que no repetiré esos usos.

Los uso bastante en código administrado (C # / .Net), y no tiene nada que ver con el ahorro de espacio, el alto rendimiento o los algoritmos inteligentes de cambio de bits. A veces, algo de lógica es muy adecuada para almacenar datos de esta manera. A menudo los uso cuando tengo una enumeración, pero las instancias pueden tomar simultáneamente múltiples valores de esa enumeración. No puedo publicar un ejemplo de código del trabajo, pero un rápido google para "Flags enum" ("Flags" es la forma C # de definir una enumeración para usar de manera bit a bit) da este buen ejemplo: http: // www.dotnetperls.com/enum-flags .


2

También hay un poco de computación paralela. Si sus datos son solo 1 y 0, puede empaquetar 64 de ellos en una palabra larga larga sin signo y obtener operaciones paralelas de 64 vías. La información genética es de dos bits (que representa la codificación AGCT del ADN), y si puede hacer los diversos cálculos en forma paralela de bits, puede hacer mucho más que si no lo hace. Sin mencionar que la densidad de datos en la memoria, si la memoria, o la capacidad del disco, o el ancho de banda de las comunicaciones es limitado, implica que se debe considerar la compresión / descompresión. Incluso los enteros de baja precisión, que aparecen en áreas como el procesamiento de imágenes, pueden aprovechar la complicada computación paralela de bits. Es todo un arte en sí mismo.


1

¿Por qué se encuentran?

Bueno, eso probablemente se deba a que corresponden a las instrucciones de ensamblaje y, a veces, solo son útiles para cosas en idiomas de nivel superior. Lo mismo se aplica al temido GOTOque corresponde a la JMPinstrucción de ensamblaje.

¿Cuáles son sus usos?

Realmente hay muchos usos para nombrar, así que solo daré un uso reciente, aunque altamente localizado. Trabajo mucho con el ensamblaje 6502 y estaba trabajando en una pequeña aplicación que convierte direcciones de memoria, valores, valores de comparación, etc. en códigos que se pueden usar para el dispositivo GameGenie (Básicamente, una aplicación trampa para el NES). Los códigos son creados por alguna manipulación de bits.


1

Muchos programadores en estos días están acostumbrados a computadoras con memoria casi infinita.

Pero algunos de los programas todavía usan pequeños microcontroladores donde cada bit cuenta (cuando solo tienes 1k o menos RAM, por ejemplo), y los operadores bit a bit le permiten a un programador usar esos bits uno por uno en lugar de desperdiciar una programación mucho más grande entidad de abstracción que podría ser necesaria para mantener algún estado requerido por el algoritmo. El IO en esos dispositivos también puede requerir ser leído o controlado en bases bit a bit.

El "mundo real" tiene muchos más microcontroladores que servidores o PC.

Para los tipos de CS puramente teóricos, las máquinas de Turing tienen que ver con bits de estado.


1

Solo uno más de los muchos usos posibles de los operadores bit a bit ...

Los operadores bit a bit también pueden ayudar a que su código sea más legible. Considere la siguiente declaración de función ...

int  myFunc (bool, bool, bool, bool, bool, bool, bool, bool);

...

myFunc (false, true, false, false, false, true, true, false);

Es muy fácil olvidar qué parámetro booleano significa qué al escribir o incluso leer el código. También es fácil perder la noción de su conteo. Tal rutina se puede limpiar.

/* More descriptive names than MY_FLAGx would be better */
#define MY_FLAG1    0x0001
#define MY_FLAG2    0x0002
#define MY_FLAG3    0x0004
#define MY_FLAG4    0x0008
#define MY_FLAG5    0x0010
#define MY_FLAG6    0x0020
#define MY_FLAG7    0x0040
#define MY_FLAG8    0x0080

int  myFunc (unsigned myFlags);

...

myFunc (MY_FLAG2 | MY_FLAG6 | MY_FLAG7);

Con nombres de bandera más descriptivos, se vuelve mucho más legible.


1

Si sabe algo sobre Unicode , probablemente esté familiarizado con UTF-8. Utiliza un montón de pruebas de bit, cambios y máscaras para empaquetar el punto de código de 20 bits en 1 a 4 bytes.


0

No los uso a menudo, pero a veces son útiles. El manejo de la enumeración viene a la mente.

Ejemplo:

enum DrawBorder{None = 0, Left = 1, Top = 2, Right = 4, Bottom = 8}

DrawBorder drawBorder = DrawBorder.Left | DrawBorder.Right;//Draw right & left border
if(drawBorder & DrawBorder.Left == DrawBorder.Left)
  //Draw the left border
if(drawBorder & DrawBorder.Top == DrawBorder.Top)
  //Draw the top border
//...

0

No estoy seguro si este uso se ha observado todavía:

Veo O bastante cuando trabajo con el código fuente illumos (openSolaris) para reducir múltiples valores de retorno a 0 o 1, por ejemplo

int ret = 0;
ret |= some_function(arg, &arg2); // returns 0 or 1
ret |= some_other_function(arg, &arg2); // returns 0 or 1
return ret;
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.