Operadores de bits
Calcular las siguientes operaciones:
11001010 & 10100101
,11001010 | 10100101
, y11001010 ^ 10100101
.
Escribir las representaciones octales y hexadecimales de los números binarios del ejercicio anterior.
Determinar los resultados octales de las siguientes operaciones, suponiendo que los números no tienen signo (los números están expresados en sistema octal):
0157
trasladado a izquierda una posición de bit,0701
trasladado a izquierda dos posiciones de bit,0673
trasladado a derecha dos posiciones de bit,067
trasladado a derecha tres posiciones de bit.
Una máscara es un patrón binario en una palabra. Las máscaras se utilizan de acuerdo con las siguientes operaciones: and, or y not. Escribir un programa que utilice una máscara para disponer 1's en posiciones que se deseen, para los bits de una palabra.
Escribir un fragmento de código/procedimiento que convierta un número entero sin signo (32 bits) a formato Big Endian, sobre un arreglo de 4 bytes. Imprimir por
stdout
las representaciones origen y destino.Ejemplo: el número
271590900
será codificado como:X[0] = 0x10 X[1] = 0x30 X[2] = 0x25 X[3] = 0xF4
Escribir un fragmento de código/procedimiento que convierta un número entero en representación Big Endian de cuatro bytes a formato decimal. Imprimir por
stdout
las representaciones origen y destino.Ejemplo: el número
|0x10|0x30|0x25|0xF4|
será decodificado como:271590900
.¿Qué aplicaciones tiene la codificación/decodificación Big Endian en Electrónica & Telecomunicaciones?
Escribir un programa que utilice una máscara para invertir los bits de determinadas posiciones.
Operadores de bits y funciones
Para un aplicativo a ejecutarse sobre una plataforma de 32 bits que contendrá una interfaz gráfica de usuario (GUI), se requiere disponer de funciones para el tratamiento de colores de los elementos gráficos de la interfaz. El color de cualquier elemento gráfico se almacenará en una variable de tipo int sin signo de 32 bits, de acuerdo a la convención RGB (ejemplo: un color con representación
RGB = 0xFFCCAA
se corresponde con0xFF
,0xCC
y0xAA
para las componentes rojo, verde y azul, respectivamente). Se pide:Escribir una función denominada
get_rojo()
que reciba como argumento el color en representación RGB sobre una variable de tipouint32_t
, que devuelva por el nombre la componente de color rojo como unuint8_t
. Dar un ejemplo de invocación de la función.Idem a. para una función denominada
get_verde()
que devuelva la componente verde. Dar un ejemplo de invocación de la función.Idem a) para una función denominada
get_azul()
que devuelva la componente Azul. Dar un ejemplo de invocación de la función.Escribir una función llamada
get_componentesRGB()
que reciba como argumentos el color en notación RGB sobre una variable de tipouint32_t
, y devuelva a través de la interfaz las 3 componentes R, G, y B simultáneamente sobre variables de tipouint8_t
. Dar un ejemplo de invocación de esta función.Escribir los prototipos de las funciones desarrolladas en los cuatro puntos anteriores.
NOTA: Se debe utilizar operadores de bits y máscaras para la resolución de este ejercicio, y documentar todas y cada una de las funciones desarrolladas.
Un microcontrolador posee el siguiente registro para el manejo de datos a través de un puerto serie que puede conectarse a una PC:
| bit 7 | 6 | 5 | 4 | 3 | 2 | 1 | bit 0 | +-------+-------+-------+-------+-------+-------+-------+-------+ | RXCIE | TXCIE | UDRIE | RXEN | TXEN | CHR9 | RXB8 | TXB8 | +-------+-------+-------+-------+-------+-------+-------+-------+
RXCIE
Cuando este bit es 1 indica que se ha terminado de recibir un dato por el puerto serie
TXCIE
Cuando este bit es 1 indica que se ha terminado de enviar un dato por el puerto serie
UDRIE
Cuando este bit es 1 habilita las interrupciones de hardware durante el envío y recepción de datos por el puerto serie
RXEN
Cuando este bit es 1 habilita al puerto serie a recibir datos, si vale 0 RXCIE no tiene funcionalidad
TXEN
Cuando este bit es 1 habilita al puerto serie a enviar datos, si vale 0 TXCIE no tiene funcionalidad
CHR9
Cuando este bit es 1 el dato tiene 9 bits de longitud más 1 bit de inicio y otro de fin. Cuando vale 0, la longitud del dato es de 8 bits más 1 de inicio y otro de fin
RXB8
Si
CHR9
es 1, este bit contiene el noveno bit del dato a ser recibidoTXB8
Si
CHR9
es 1, este bit contiene el noveno bit del dato a ser transmitidoSe pide:
Escriba el código de una función denominada
transmision_completa()
que indique, a partir del contenido del byte de control (bitTXCIE
), y mediante la devolución de un valor por el nombre, si se ha completado o no, la transmisión de un símbolo por el puerto serie. El prototipo de la función pedida es:bool transmision_completa(uint8_t);
Dar un ejemplo de invocación de la función.
Modifique el código de la función pedida en el punto a) para que la misma indique, mediante la devolución de un valor por la interfaz, si se ha completado o no la transmisión. Dar un ejemplo de invocación de esta función. Debe utilizarse máscaras y operadores de bits para leer cada bit del registro de datos.
Un sintetizador de una radio digital posee un generador de frecuencia controlado por el siguiente registro:
| bit 7 | 6 | 5 | 4 | 3 | 2 | 1 | bit 0 | +-------+-------+-------+-------+-------+-------+-------+-------+ | AFT | BNDAM | E | D | C | B | A | SYN | +-------+-------+-------+-------+-------+-------+-------+-------+
AFT
Control de Sintonía Fina Automática (1 = ON , 0 = OFF).
BAND
Control de selección de banda AM (1 = AM , 0 = FM).
E, D, C, B, A
Factor de división de la frecuencia de reloj (ver tabla).
SYN
Control de activación del sintetizador (1 = ON, 0 = OFF).
E
D
C
B
A
Factor de división (FDIV)
0
0
0
0
0
1
0
0
0
0
1
2
0
0
0
1
0
3
0
0
0
1
1
4
...
...
...
...
...
...
1
1
1
1
0
31
1
1
1
1
1
32
Se pide:
Escriba el código de una función denominada
get_synthesizer_divider()
que devuelva por su nombre el factor de división a partir de los bits A, B, C, D y E contenidos en el byte de control. El prototipo de la función pedida es:int get_synthesizer_divider(uint8_t);
Dar un ejemplo de invocación de la función.
Escriba el código de una función denominada
es_am()
que reciba como parámetro el registro de control, y retorne por si está seleccionada la banda de AM. Dar un ejemplo de invocación.
NOTA: Debe utilizarse máscaras y operadores de bits para operar con los bits del registro de datos.
Para un subsistema de comunicaciones basado en un microcontrolador, que forma parte de un equipo de medición, se debe programar el registro de control de periféricos (SPCR), el cual tiene la siguiente estructura:
| bit 7 | 6 | 5 | 4 | 3 | 2 | 1 | bit 0 | +-------+-------+-------+-------+-------+-------+-------+-------+ | SPIE | SPE | - | - | CPOL | SPR1 | SPR0 | - | SPCR +-------+-------+-------+-------+-------+-------+-------+-------+
en donde:
SPIE
control de interrupciones (1: interrupciones habilitadas, 0: deshabilitadas)
SPE
encendido del periférico (1: encendido, 0: apagado)
CPOL
control de polaridad (1: clock activo por nivel bajo, 0: clock activo por nivel alto)
SPR1 & SPR0
factor de división del reloj interno, de acuerdo a la siguiente tabla:
SPR1
SPR0
Factor de división
Frecuencia de clock
0
0
2
1MHz
0
1
4
500kHz
1
0
16
125kHz
1
1
32
62.5kHz
Se pide:
Escribir dos funciones que devuelvan el estado de los bits SPIE y CPOL respectivamente, y que respondan a los siguientes prototipos:
bool getSPIE(uint8_t); bool getCPOL(uint8_t);
Escribir una función
getCOMControl()
que devuelva simultáneamente por la interfaz el estado de los bits SPIE, y CPOL, y del factor de división del clock interno (esta función no debe devolver nada por el nombre). Escribir su prototipo.Escribir una función cuyo prototipo sea:
setCPOL(uint8_t *control, bool on);
que permita colocar el bit CPOL en 1 ó 0, dependiendo si el valor del argumento on estrue
ofalse
, respectivamente.Dar un ejemplo de invocación de cada una de las funciones desarrolladas en los puntos anteriores.
NOTA: Se deben utilizar operadores de bits y máscaras para la resolución de este ejercicio.
Para un programa que hará uso del puerto paralelo de una PC se necesita disponer de funciones que permitan colocar a nivel lógico
1
ó0
un bit determinado dentro de un byte a ser presentado al puerto paralelo. Se pide:Escribir una función llamada :ansi:
set()
, que obedezca al siguiente prototipo:uint8_t set(uint8_t datos, short linea);
que reciba un byte sobre la variable
datos
y el número de línea que será forzada a nivel lógico1
, devolviendo por el nombre el resultado de la operación.Ejemplo: si en
datos
se recibe1000 0001
y linea = 3, se debe retornar el byte1000 1001
.Dar un ejemplo de invocación de la función.
Idem a) pero para una función llamada
clear()
que coloque a nivel lógico0
la línea indicada, y devuelva por el nombre el resultado de la operación. Ejemplo: Si endatos
se recibe0111 1100
y línea = 3, se debe devolver el byte0111 0100
.Dar un ejemplo de invocación de la función.
ACLARACION: La línea 0 se corresponde con el bit menos significativo (LSB) del byte, y la linea 7 con el bit más significativo (MSB) del byte.
La arquitectura de procesadores x86 tiene un conjunto de instrucciones llamado Bit Manipulation Instructions sets (BMI) que implementa operaciones de bits. Implementar en C algunas de ellas:
int lzcnt(uint32_t n);
(leading zeros count), cuenta la cantidad de ceros que hay a partir del MSB hasta encontrar el primer uno. Ejemplo: Si se recibe0010 0000
debe devolver el valor 26 (no olvidar quen
es de 32 bits y el ejemplo muestra sólo el byte más liviano).int popcnt(uint32_t n);
(population count), cuenta la cantidad total de unos. Ejemplo: Si se recibe1100 1010
debe devolver 4.uint32_t bextr(uint32_t a, uint16_t b);
(bit field extract) el byte más liviano deb
es un inicio y el más pesado una longitud, extrae longitud bits dea
a partir de esa posición. Ejemplo:a = 1001 0011 1011 1101
,b = 0000 1000 0000 0100
, en este caso el inicio es0000 0100
, es decir 4 y la longitud es0000 1000
, es decir 8. Entonces se extraerán 8 bits dea
desde la posición 4 y se devolverá0011 1011
.uint32_t blsi(uint32_t n);
(extract lowest set isolated bit) devuelve una máscara para el bit menos pesado que esté encendido. Ejemplo: Para0101 1100
debe devolver0000 0100
.uint32_t blsmsk(uint32_t n);
(get mask up to lowest set bit) devuelve una máscara que llega hasta el bit menos pesado que esté encendido. Ejemplo: Para0101 1100
debe devolver0000 0111
.uint32_t blsr(uint32_t n);
(reset lowest set bit) apaga el bit menos pesado que esté encendido. Ejemplo: Para0101 1100
debe devolver0101 1000
.int tzcnt(uint32_t n);
(trailing zeros count), cuenta la cantidad de ceros que hay a partir del LSB hasta encontrar el primer uno. Ejemplo: Si se recibe0010 0000
debe devolver el valor 5.uint32_t pext(uint32_t a, uint32_t m);
(parallel bits extract) extrae dea
los bits indicados por la máscaram
. Ejemplo, sia = 1011 1001
ym = 1001 1100
se deben extraer dea
los bits1--1 10--
por lo que se devolverá0000 1110
.uint32_t pdep(uint32_t a, uint32_t m);
(palallel bits deposit) setea los bits dea
indicados en las posiciones de la máscaram
. Ejemplo, sia = 0000 1101
yb = 1100 0110
la máscara enmascara 4 bits, que son los más livianos dea
, entonces los bits1101
dea
se distribuirían segúnb
así11-- -01-
por lo que se devolverá1100 0010
.
El algoritmo de suma de validación (checksum) de BSD realiza estos pasos operando su suma en 16 bits:
Inicializa el checksum en 0.
Para cada carácter en el texto:
Rota el checksum a la derecha (es decir, desplaza todos los bits en el sentido del bit menos significativo, y el bit que se cae entra en la posición más significativa --15--).
Suma el carácter al checksum.
Verifica que el checksum mantenga 16 bits enmascarándolo con
0xFFFF
.
Devuelve el checksum.
Implementar la función
int bsd_checksum(const char *s);
que compute el checksum de la cadenas
.Linear-feedback shift register (LFSR) es un método para generar números pseudoaleatorios. En este método se tiene un registro que en cada iteración se desplaza hacia la derecha. El bit aleatorio de la iteración es el LSB, mientras que el MSB se alimenta con una combinación lineal de bits del registro. Eligiendo correctamente los bits que se combinan este método garantiza que para un registro de \(n\) bits, e inicializando el registro en un valor distinto de cero, se puede ciclar por \(2^n-1\) estados diferentes; es decir, se pasará por todos los valores posibles con excepción del cero antes de repetir la secuencia.
Por ejemplo, para un registro de 4 bits, inicializado en
1001
y tomando como nuevo MSB la combinación de los bits 0 y 1 en el primer paso la salida será un1
por ser ese el valor del LSB, y se introducirá un1
como MSB dado que los bits 0 y 1 son1
y0
respectivamente y que1 ^ 0 = 1
. Entonces en el segundo paso el registro quedará como1100
, el resultado de desplazar a la derecha e introducir el1
calculado.Si se sigue adelante con la secuencia la misma será:
Paso Registro Salida Realimentación 1 1001 1 0 ^ 1 = 1 2 1100 0 0 ^ 0 = 0 3 0110 0 1 ^ 0 = 1 4 1011 1 1 ^ 1 = 0 5 0101 1 0 ^ 1 = 1 6 1010 0 1 ^ 0 = 1 7 1101 1 0 ^ 1 = 1 8 1110 0 1 ^ 0 = 1 9 1111 1 1 ^ 1 = 0 10 0111 1 1 ^ 1 = 0 11 0011 1 1 ^ 1 = 0 12 0001 1 0 ^ 1 = 1 13 1000 0 0 ^ 0 = 0 14 0100 0 0 ^ 0 = 0 15 0010 0 1 ^ 0 = 1 1 1001 ...
donde puede verse que la secuencia resultó ser de 15 valores, es decir \(2^4-1\). Puede además observarse que la salida fueron 8 unos y 7 ceros.
Implementar:
Una función
bool lfsr4();
que implemente el algoritmo de LFSR en 4 bits combinando los bits 0 y 1. Cada llamada deverá devolver un bit de la salida. Para el registro se puede utilizar una variablestatic
dentro de la función inicializada en un valor diferente a cero.Una función
bool lfsr8();
que implemente el algoritmo para 8 bits combinando los bits 0, 2, 3 y 4 (se debe realizar el xor entre los cuatro valores).Una función
bool lfsr16();
que implemente el algoritmo para 16 bits combinando los bits 0, 1, 3 y 12.Una función
bool lfsr24();
que implemente el algoritmo para 24 bits combinando los bits 0, 1, 2 y 7.Comprobar que las 4 funciones desarrolladas requieran \(2^n-1\) llamadas hasta repetir el valor de inicialización del registro. Para hacer esto se puede agregar un contador
static
dentro de las funciones y verificar su valor cuando el registro vuelva al valor inicial.
Para detectar errores de transmisión o corrupción en el almacenamiento de datos muchos sistemas utilizan un chequeo de redundancia cíclico (CRC). Un CRC es un grupo de \(n\) bits que son redundantes con el valor que se pretende codificar. Para generar estos \(n\) bits se utilizan los \(n + 1\) coeficientes (binarios) de un polinomio de grado \(n\).
Por ejemplo para agregarle 3 bits de redundancia a un valor la codificación CRC-3-GSM, utilizada en telefonía celular, usa el polinomio de grado tres: \(x^3 + x + 1\). Es decir sus coeficientes pueden escribirse como \([1, 0, 1, 1]\), o según el binario
1011
.Teniendo la representación binaria del polinomio y el valor de codificar lo que se hace en primer lugar es agregar \(n\) bits en cero en las posiciones menos pesadas del valor a codificar y luego repetidas veces se alinea el polinomio junto al bit más pesado que esté en uno y se computa el xor entre los bits del polinomio y los del valor, dejando inalterados los demás.
Por ejemplo, para el valor en 12 bits
100100111011
el mismo se completa con tres ceros a derecha como100100111011000
. La primera operación de xor que se realizará será:100100111011 000 1011 ---------------- 001000111011 000
Notar que el resultado es la concatenación del xor para los 4 bits más significativos y el valor original para el resto. Como el polinomio tiene un coeficiente 1 en su valor más significativo y como el mismo se alinea siempre con el 1 más pesado del valor entonces siempre la operación va a hacer aparecer al menos un cero adicional al comienzo del resultado. La segunda iteración será:
001000111011 000 1011 ---------------- 000011111011 000
Y las siguientes:
000011111011 000 1011 ---------------- 000001001011 000 1011 ---------------- 000000010011 000 1011 ---------------- 000000000101 000 101 1 ---------------- 000000000000 100
Como ya no quedan unos en los 12 bits del valor original se corta el algoritmo. El CRC-8-GSM para este valor será
100
, el resultado que queda en los bits agregados.Implementar:
Implementar una función
size_t bit_mas_pesado(unsigned int i);
que reciba un enteroi
y devuelva en qué posición está su uno más pesado, por ejemplo, para el entero1011
el uno más pesado está en la posición3
. Si el entero fuera igual a cero devolver0
.Implementar una función
unsigned int operar(unsigned int valor, unsigned int polimomio);
que realice un paso del algoritmo de CRC, se recibe el valor (con los n bits para almacenar la redundancia ya en su lugar) y un polinomio. Se debe computar el resultado de aplicar xor en los \(n + 1\) bits del polinomio dejando inalterados los bits restantes del valor. Por ejemplo para el valor1000111011000
y el polinomio1011
debe devolverse11111011000
.Implementar una función
unsigned int calcular_crc(unsigned int valor, unsigned int polinomio);
que reciba un valor, un polinomio y devuelva su CRC. Por ejemplo, para el valor100100111011
y el polinomio1011
debe devolverse100
.Implementar una función
bool es_crc_valido(unsigned int valor, unsigned int polinomio);
que reciba un valor que tiene su CRC puesto en sus n bits más significativos y un polinomio y que devuelva si el CRC se corresponde con el resto del valor, por ejemplo para el valor de 16 bits1000100100111011
, donde el CRC es100
, el valor es0100100111011
y el polinomio1011
debe devolversetrue
.
Aclaración: Notar que la cantidad de bits del CRC puede obtenerse del resultado de
bit_mas_pesado(polinomio)
.El formato de codificación de caracteres UTF-8 permite representar símbolos hasta de 21 bits usando entre 1 y 4 bytes para representar cada carácter.
Un carácter puede tener 1, 2, 3 o 4 bytes representándose de la siguiente forma:
1 byte: 0xxxxxx, donde el valor se conforma por esos 7 bits y se corresponden con la tabla ASCII, es decir los valores entre 0x00 y 0x7F.
2 bytes: 110xxxxx 10xxxxxx, donde los 11 bits codifican al carácter, siendo válida cualquier combinación entre 0x80 y 0x7FF (los valores de entre 0x00 y 0x7F no son válidos porque deberían haber sido representados utilizando sólo un byte).
3 bytes: 1110xxxx 10xxxxxx 10xxxxxx, donde los 16 bits codifican al carácter siendo válidos los símbolos entre 0x800 y 0xFFFF.
4 bytes: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx, donde los 21 bits codifican al carácter siendo válidos los símbolos entre 0x10000 y 0x10FFFF (notar que no se utiliza la totalidad de los 21 bits).
En la codificación UTF-8 el valor numérico del carácter se denomina code point. A continuación se muestra una tabla de ejemplo de representaciones de distintos caracteres UTF-8 y su representación como bytes y como codepoints:
En este trabajo vamos a considerar que un carácter normal está codificado en un
char
(donde varios char pueden representar un carácter UTF-8), y un codepoint se codifica en unuint32_t
. Se deja al alumno investigar sobre el tipowchar_t
del estándar.Se pide:
Implementar una función
size_t strlen_utf8(const char s[]);
que reciba una cadena de caracteres y devuelva su longitud considerando que los caracteres son UTF-8.Implementar una función
bool es_utf8(const char s[]);
que reciba una cadena de caracteres y diga si es una cadena UTF-8 válida.Implementar una función
bool char_to_utf8(const char s[], uint32_t codepoints[]);
que dada una cadena de caracteress
convierta cada uno de los caracteres de la misma en sus valores UTF-8 y los almacene encodepoints
, deberá convertirse hasta el'0'
inclusive por lo que el último codepoint será el0
.Implementar una función
bool utf8_to_char(uint32_t codepoints[], char s[]);
que convierta el vectorcodepoints
en su representación como bytes conforme a UTF-8 y lo almacene en la cadenas
.
Dentro de la tabla de caracteres UTF-8 los codepoints del rango
0x2800
al0x28FF
se utilizan para representar las 256 combinaciones posibles de los caracteres del Braile con cuatro filas y dos columnas de puntos.Las mismas están numeradas:
0 3 1 4 2 5 6 7
Por ejemplo, el codepoint
0x28A7
representa el carácter⢧
, es decir, tiene los bits 0, 1, 2, 5 y 7 prendidos:10100111
.Si bien no es el objetivo de estos caracteres, los mismos pueden utilizarse para representar 8 pixeles de una imagen en la terminal.
Implementar una función
uint32_t imagen4x2_a_codepoint(const bool imagen[4][2]);
que dada una matriz de pixeles devuelva el codepoint correspondiente a esa imagen.Implementar una función
void codepoint_braile_a_char(uint32_t codepoint, char caracteres[3]);
que convierta uncodepoint
del rango del Braile a su representación en UTF-8 y la devuelva encaracteres
(ver el ejercicio anterior, y notar que los caracteres del Braile están en el caso de 3 bytes).Haciendo uso de la función del ítem b, reimplementar la función del ítem a para que sea:
void imprimir_imagen(const bool **imagen, size_t ancho, size_t alto);
que reciba una imagen de tamaño arbitrario (puede asumirse queancho
yalto
son múltiplos de 2 y 4 respectivamente) y la imprima en UTF-8 porstdout
.
Si se desea se puede retomar el formato PBM de imágenes descripto en este ejercicio e implementar un programa que lea una imagen en formato PBM y la imprima con caracteres Braile por
stdout
.En muchas aplicaciones suele ser conveniente en vez de almacenar números en binario almacenarlos en decimal. Para esto se suele utilizar el formato BCD, en el cual cada 4 bits se representa un dígito decimal entre 0 y 9. Por ejemplo, el número
9511
decimal puede representarse con 16 bits en BCD como1100 0101 0001 0001
(o, dicho de otra forma, como el0x9511
), donde cada nibble es un dígito.Implementar la función
int extraer_digito(uint64_t bcd, size_t n);
que devuelva el dígiton
delbcd
. Por ejemploextraer_digito(0x9511, 3) -> 9
.Implementar la función
uint64_t sumar_digito(uint64_t bcd, size_t n, int suma);
que devuelva el resultado de sumarle el valorsuma
al dígiton
delbcd
. Asumir que el dígito más la suma va a ser siempre menor a 16, por lo que nunca el resultado se va a escapar de los 4 bits del dígito. Por ejemplosumar_digito(0x9511, 2, 5) -> 0x9a11
.Nota: dado que
suma
es signado esta función también funcionará para restar si asumimos que el resultado de la resta del dígito sea mayor o igual a cero. No hay que hacer nada adicional.Implementar la función
uint64_t incrementar_bcd(uint64_t bcd);
que devuelva el resultado de sumarle uno albcd
dado. Por ejemploincrementar_bcd(0x399) -> 0x400
.(Difícil) Implementar la función
uint64_t sumar_bcd(uint64_t a, uint64_t b);
que devuelva el resultado de sumar los dos BCDsa
yb
. Por ejemplosumar_bcd(0x9511, 0x7502) -> 0x17013
(notar que no puede utilizarsesumar_digito()
dado que no está garantizado que la suma de dos dígitos dé menos de 16).
El algoritmo double dabble permite convertir números binarios en números BCD realizando operaciones de shifts y sumas sobre dígitos únicamente.
Para convertir un número de
n
bits hay que reservar una cantidad de dígitos BCD suficientes para realizar la conversión e inicializarlos en 0. Por ejemplo, supongamos que queremos convertir un número de 8 bits, dado que el número más grande que puede representarse es 255 entonces necesitamos 3 dígitos BCD.Vamos a convertir el número 243 a BCD. El número 243 se representa como
11110011
en binario. Inicializamos un registro con el siguiente contenido:0000 0000 0000 11110011
Con los 3 dígitos y los 8 bits. Con una variable con al menos 20 bits alcanza.
Partiendo desde esa posición inicial se realizan tantas iteraciones del algoritmo como bits tenga el binario. En este caso serán 8 iteraciones.
Cada iteración tendrá dos pasos:
A cada dígito del BCD que valga 5 o más se le suma 3.
Se realiza un shift a la izquierda de todo el conjunto.
Los 8 pasos entonces serán:
0000 0000 0001 11100110
, como todos los BCD valían cero sólo shifteamos.0000 0000 0011 11001100
, igual al anterior.0000 0000 0111 10011000
, ídem, notar que ahora el dígito 0 vale 7.0000 0000 1010 10011000
, sumamos 3 al dígito 0.0000 0001 0101 00110000
, shift.
0000 0001 1000 00110000
, sumamos 3 al dígito 0 que valía 5.0000 0011 0000 01100000
, shift.
0000 0110 0000 11000000
, sólo shift, ningún dígito valía más de 5.0000 1001 0000 11000000
, sumamos 3 al dígito 1 que valía 6.0001 0010 0001 10000000
, shift.
0010 0100 0011 00000000
, shift.
Terminados los 8 pasos los dígitos BCD son 243.
(En el artículo de Wikipedia enlazado al comienzo hay más ejemplos, con más bits.)
Implementar:
Una función
uint64_t convertir_a_bcd(unsigned int n24);
que convierta el númeron24
de 24 bits en un número BCD según el algoritmo de double dabble.Notar que \(2^{24} - 1 = 16\,777\,215\) por lo que utilizando 64 bits para operar hay espacio más que suficiente para los 24 bits del binario y los 8 dígitos BCD.
Notar además que como 24 es múltiplo de 4 los dígitos BCD están bien alineados por lo que pueden utilizarse todas las funciones desarrolladas en el ejercicio anterior.
El algoritmo de double dabble es muy sencillo de revertir. Se inicializa un registro como el del algoritmo original con los dígitos del BCD y el binario puesto en cero. En cada paso se realiza un shift a la derecha y luego a cada dígito BCD mayor o igual a 8 se le resta 3. Después de tantos pasos como bits en el binario se obtiene el binario. (En el artículo de Wikipedia está el seguimiento para revertir 243.)
Implementar la función
unsigned int convertir_a_binario(uint64_t bcd);
que reciba unbcd
de no más de 8 dígitos y lo convierta a la representación binaria en 24 bits.