Operadores a nivel de bits (lenguaje C).

Iniciado por NOB2014, 16 Marzo 2014, 02:48 AM

0 Miembros y 2 Visitantes están viendo este tema.

Yoel Alejandro

Ok, creo NOMB2014 que la intención de la "máscara" en los programas anteriores era imprimir un número en binario (por eso putchar() que imprime carácter a carácter). Para que la explicación sea más sencill pondré un ejemplo con un char (un byte).

Si queremos imprimir en binario tenemos que escanear cada uno de sus bits e imprimir un '1' ó un '0' según el caso. Pongamos el ejemplo del numero:

num = 1000 1001

Entonces comenzamos con la máscara mask = 1000 0000 (0x80 en hexadecimal). Hacemos el AND binario entre el número y la máscara. Observa bien que la máscara tendrá un único bit en 1, los demás en 0. Si el AND binario da como resultado distinto de cero es porque el número original posee un '1' en la misma posición de la máscara. Así que el bit más significativo del número es '1':

num & mask = 1000 1001 & 1000 0000 = 1000 0000

Ahora seguimos recorriendo el número de izquierda a derecha, para ellos usamos la máscara mask = 0100 0000 (0x40 en hexadecimal). El resultado será '0', porque el número posee un bit '0' en la misma posición donde la máscara tiene su único 'bit 1':

num & mask = 1000 1001 & 0100 0010 = 0000 0000

Y así ya conoces el valor de los dos bits más significativos de num. Siguiendo con las máscaras 0010 0000,  0001 0000, etc., conocerás los demás bits.

Observa que las máscaras sucesivas se obtienen fácilmente empezando con 1000 0000 y rotando a la derecha tantas veces como uno menos que la cantidad total de dígitos binarios que posea el número.

Así, un programa para imprimir en binario un número char sería, donde por sencillez sustituyo el operador ternario "?:" por un "if" estándar:
Código (cpp) [Seleccionar]

void imprimir(unsigned char num)
{
unsigned char mask = 0x80; /* la mascara */
size_t i;

for (i = 0; i < sizeof(char) * CHAR_BIT; i++) {
if ( num & mask )
putchar('1');
else
putchar('0');
mask >>= 1;
}
}


=============
OBSERVACIONES: 1. Una forma de inicializar la máscara hubiera sido mask = 0x01 << 7, o sea agarrar el 0x01 y rodar el '1' a la izquierda 7 veces. O más consistentemente, mask = 1 << sizeof(char) * CHAR_BIT - 1. Digo que la segunda forma es más consistente, porque se puede cambiar fácilmente el tipo char por short int, o por int donde el tamaño del número en bits es determinado automáticamente por el compilador en lugar de hacerlo nosotros manualmente.

2. El importante el tipo de la máscara como "unsigned". Pues según las especificaciones para enteros signados negativos el comportamiento de la operación ">>" queda indeterminado. Los bits desplazados en la parte izquierda del número pudieran ser rellenados por '1', o por '0' dependiendo del sistema.
Saludos, Yoel.
P.D..-   Para mayores dudas, puedes enviarme un mensaje personal (M.P.)

NOB2014

Hola a todos.
Como siempre en este foro la ayuda es muy abundante y esmerada, con los últimos 2 post me queda bien claro como imprimir un número en binario, que es en definitiva lo que quería aprender.-
En definitiva voy a tratar de interpretar el código de rir3760, voy a correrlo, pero de principio no entiendo que valores arrojan size_t i y size_t j  y para que el doble for, ya me pongo a buscar información.-

Saludos.
Daniel
abraza las cosas y personas malas como si fueran tu mas preciada joya,Son tus mas grandes maestros de paciencia sabiduría y amor y cuando lo abrazas dejan de causar dolor.-

Yoel Alejandro

Para abreviarte un poco el trabajo, lo que rir3760 es un doble "for" anidado para imprimir el número en grupos de a 8 bits (o sea, bytes), con un espacio entre cada byte, e.g.:

11000001 00010011 10100110 00010111

El for con el contador "j" imprime cada octeto y el for con el contador "i" pasa de un octeto a otro. Mira que la instrucción sizeof(unsigned) te da el tamaño de un unsigned (que es el mismo tamaño del int) en bytes, que es 4 para el caso de una máquina con enteros de 32 bits. Este es el rango de i. Luego, la macro CHAR_BIT da el número de bits que ocupa un byte, o sea 8 en nuestro caso. Este es el rango de j.

De no haber usado este anidamiento el número se hubiera impreso con todos los bits "pegados", o sea, menos estéticamente. Otra solución, con un solo contador, tal vez hubiera sido intercalar un espacio luego del bit 7, luego del bit, 15, y luego del 23, con el operador módulo "%". Si el número más 1 es divisible por 8, dejar el espacio:
Código (cpp) [Seleccionar]

void imprimir(unsigned num)
{
  unsigned msb = 1U << sizeof(unsigned) * CHAR_BIT - 1;
  size_t i;
  size_t j;

  for (i = 0; i < sizeof(unsigned) * CHAR_BIT; i++){
     putchar(num & msb ? '1' : '0');
     msb >>= 1;
     
     /* aqui el espacio */
     if ( i > 0 && (i + 1)%8 == 0 )
        putchar(' ');
  }
  putchar('\n');
}

donde la condición i > 0 es para que no imprima espacio antes del bit número cero (el primero).

Por último, el tipo size_t es un tipo aritmético, normalmente un entero no signado, devuelto por el operador sizeof() y usado en funciones como strlen(), fread(), etc. Para decirlo de un modo sencillo, usamos ese tipo para enumerar cantidades "posiblemente grandes" de bytes. En el caso de rir3760 se definió "i" de tipo size_t porque ese es el tipo devuelto por la expresión sizeof(unsigned).

Se que dijiste que ibas a investigar todo esto, y deseo que lo hagas pero no me costaba nada dedicar 5 minutos a aclarar unas cosas que te hubieran demandado quiźa 2 horas (y así usas esas 2 horas en investigar otros temas  ;D).
Saludos, Yoel.
P.D..-   Para mayores dudas, puedes enviarme un mensaje personal (M.P.)

eferion

if ( i > 0 && (i + 1)%8 == 0 )

O más sencillo.

if ((i + 1)%8 == 0 )

Todo i en el rango ( 0..31 ) que verifica "(i+1)%8==0" es necesariamente mayor que 0, puesto que (0+1)%8 == 1%8 == 1 != 0.

;)

rir3760

Cita de: NOB2014 en 19 Marzo 2014, 13:59 PMde principio no entiendo que valores arrojan size_t i y size_t j  y para que el doble for
Eso ya lo explico de una forma mas que satisfactoria yoel_alejandro.

----

En cuanto al condicional:
if ( i > 0 && (i + 1)%8 == 0 )
eferion ya indico como acortarlo, solo agregar que (por consistencia) se debe utilizar la macro CHAR_BIT:
if ((i + 1) % CHAR_BIT == 0) ...

Un saludo
C retains the basic philosophy that programmers know what they are doing; it only requires that they state their intentions explicitly.
--
Kernighan & Ritchie, The C programming language

NOB2014

Hola a todos.
CitarSe que dijiste que ibas a investigar todo esto, y deseo que lo hagas pero no me costaba nada dedicar 5 minutos a aclarar unas cosas que te hubieran demandado quiźa 2 horas (y así usas esas 2 horas en investigar otros temas  ;D
No te das una idea lo que se estima estas frases cuando uno tiene 62 años y a nadie al alcance para pedir ayuda, lo digo más que nada por la seguridad que brinda el hecho de saber que podes levantar el tubo y tener la respuesta a tú duda o mandar un mail y del otro lado contar con un amigo que tiene todas las respuestas a tus inquietudes, bueno por algo será, por lo menos cuento con Uds.-
Gracias eferion y a vos rir3760 por la participación, en cuanto a este tema para mí lo podemos dar por solucionado.-

Saludos.
Daniel

abraza las cosas y personas malas como si fueran tu mas preciada joya,Son tus mas grandes maestros de paciencia sabiduría y amor y cuando lo abrazas dejan de causar dolor.-

Yoel Alejandro

Saludos, Yoel.
P.D..-   Para mayores dudas, puedes enviarme un mensaje personal (M.P.)