/* */

23 de mayo de 2008

Capítulo 3

.
Operadores Binarios

Con estos operadores podemos realizar operaciones a nivel de bit sobre variables de tipo entero: byte (1 byte), word (2 bytes), shortint (1 byte con bit de signo), integer (2 bytes con bit de signo) y longint (4 bytes con bit de signo).

Recordemos que un número entero, que nosotros vemos y expresamos en decimal, internamente tiene una representación binaria. Por ejemplo:

var a,b: integer;
begin
...a := 77; // en binario es: |00000000|01001101|
...b := -77; // en binario es: |10000000|01001101|
end.

Vemos que tanto a como b se representan en dos bytes (son de tipo integer). El bit más significativo (el más a la izquierda) se utiliza para representar el signo (ya que integer es un tipo signado): 1 indica que el número es negativo mientras 0 (cero) indica que el número es positivo.

Supongamos ahora que la variable a la definimos de tipo byte (no signado) y le asignamos el valor 77. Su representación binaria será:


Se lo representa en un único byte y sin bit de signo. El bit más a la izquierda ahora representa un valor numérico por lo tanto en una variable de tipo byte podemos almacenar valores entre 0 y 255.

Vemos también que cada bit representa una potencia de 2. El bit más a la derecha (el menos significativo) representa la potencia cero (2 a la cero), el siguiente (hacia la izquierda) representa la potencia uno (2 a la uno) y así sucesivamente. El valor 77 (decimal) se obtiene sumando las potencias de dos cuyos bits valen 1 (pintados en celeste).

Luego de esta introducción podemos analizar los operadores binarios.


Operadores OR y AND binarios

Sean las siguientes definiciones:

var x,y,z,t: byte;
begin
...x := 77; // en binario es: |01001101|
...y := 194; // en binario es: |11000010|
...z := x OR Y; // ver explicacion
...t := x AND y; // ver explicacion
end.

La variable z tendrá un 1 en cada posición en la que x o y (o ambos) tengan 1 ya que el operador OR unirá los unos de ambos operandos.

x := 01001101
y := 11000010
z := 11001111

Entonces el resultado de x OR y (que asignamos a z) será: 207.

La variable t (a la que le asignamos el resultado de x AND y) solo tendrá un 1 en aquellas posiciones en las que ambos operandos tengan 1.

x := 01001101
y := 11000010
t := 01000000

En este caso la variable t quedará con el valor 64.


Operador NOT

var x,y,z,t: byte;
begin
...x := 77; // en binario es: |01001101|
...y := NOT x; // ver explicacion
...z := 0; // en binario |00000000|
...t := NOT z; // ver explicacion
end.

El operador NOT "niega" cada posición del byte asignando 1 en aquellas posiciones en las que el byte tiene cero y cero en las posiciones en las que el byte tiene 1. Entonces:

a x le asignamos 77 y a y le asignamos NOT x:

x := 01001101
y := 10110010

y queda con 178. Luego a z le asignamos 0 y a t le asignamos NOT z:

z := 00000000
t := 11111111

t queda con 255.


Desplazamiento (shift) de Bits

Los operadores de dezplazamiento permiten correr hacia la izquierda o hacia la derecha los bits de un número no signado.

var x,y,z,t: byte;
begin
...x := 24; // en binario es: |00011000|
...y := x >> 2; // el resultado es: |00000110|
...z := x << 2; // el resultado es: |01100000|
...t := x << 3; // el resultado es: |11000000|
end.

Como vemos, la "doble flecha" indica la dirección (izquierda o derecha) en la que queremos desplazar los bits del número binario. En este ejemplo: y queda con el valor 6, z queda con 96 y t queda con 192.


Problema
Una empresa toma la desición de que las fechas dentro de sus sistemas queden representadas en 2 bytes con el siguiente formato: Los 7 bits "más a la izquierda" representarán el año, los siguientes 4 bits (hacia la derecha) representarán el mes y los restantes 5 bits (hacia la derecha) representarán el día.



Se pide

1 - Desarrollar tres funciones llamadas getAnio, getMes y getDia, que reciban una fecha expresada en un word (2 bytes sin signo) y retornen el año, el mes y el día (respectivamente) representados en la fecha pasada como parámetro.

2 - Desarrollar tres procedimientos llamados setAnio, setMes y setDia, que reciban un var word (que representa una fecha) y un valor que indicará el año, mes o día (respectivamente) que se desea asignar en la fecha recibida por referencia. Estas funciones permiten facilmente componer una fecha con el formato definido.

Nota: El año debe considerarse de la siguiente manera: sea a el valor expresado en los 7 bits correspondientes al año entonces:

año = 2000 - 50 + a

Es decir: Si a vale 50 representa al año 2000. Si a vale 51 representa al año 2001. Si a vale 52 representa al año 2002, si vale 49 representa al año 1999 y así sucesivamente.


Análisis

Desarrollaremos las seis funciones en una unit llamada untFechasBin.pas. Antes de comenzar analizaremos la sección interface de la unit para dejar en claro sus encabezados.

untFechasBin.pas
   1:
2:unit untFechasBin;
3:
4:interface
5:
6: const
7: MASK_ANIO = 65024; // |11111110|00000000|
8: MASK_MES = 480; // |00000001|11100000|
9: MASK_DIA = 31; // |00000000|00011111|
10:
11: // funciones para setear la fecha
12: procedure setAnio(var fecha:word; a:word);
13: procedure setMes(var fecha:word; m:word);
14: procedure setDia(var fecha:word; d:word);
15:
16: // funciones para leer una fecha
17: function getAnio(fecha:word):word;
18: function getMes(fecha:word):word;
19: function getDia(fecha:word):word;
20:
21:implementation
22:
23:// :
24:// : desarrollado mas abajo
25:// :
26:
27:end.
28:

Primero analizaremos un programa principal que invoque a las funciones para componer una fecha (en este caso la fecha 2 de octubre de 2008):

testFechasBin.pas
   1:
2:uses untFechasBin;
3:
4:var f:word;
5:begin
6: f:=0;
7:
8: setDia(f,2);
9: setMes(f,10);
10: setAnio(f,2008);
11:
12: writeln('Dia: ', getDia(f));
13: writeln('Mes: ', getMes(f));
14: writeln('Anio: ', getAnio(f));
15:end.
16:

El formato definido para las fechas asigna 5 bits para representar el día. En 5 bits podemos almacenar números comprendidos entre cero y "(2 a la n) -1", es decir: 31. Para representar el mes se asignaron 4 bits que permiten un rango numérico de entre cero y 15. Por último, para representar el año se asignan 7 bits con los que podemos representar valores de entre 0 y 127. Como este rango no es suficiente para representar un año de 4 dígitos se definió la regla indicada más arriba (50 representa al año 2000, etc).

función getDia

Esta función es la más fácil de resolver. Los bits que representan al día son los 5 bits más a la derecha de los dos bytes del word que contiene la fecha. Si pudiésemos asignar cero a todos los otros bits y dejar intactos estos 5 bits, el valor decimal resultante será el día que tenemos que retornar.

Para asignar cero a todos los otros bits y dejar intactos los 5 bits que representan el día tenemos que hacer un AND entre la fecha y un número que tenga unos en los primeros 5 bits y ceros en los bits restantes. Como estamos utilizando tipos word entonces "fabricamos" nuestro número en 2 bytes:

|00000000|00011111|

Este número binario representa al valor decimal: 31. Decimos que es una "máscara para día". Si hacemos un AND entre una fecha (un word) y 31 estaremos dejando intactos los primeros 5 bits y asignando ceros a los bits restantes.
1:
2:function getDia(fecha:word):word;
3:begin
4: getDia:=fecha AND MASK_DIA;
5:end;
6:

En la sección interface definimos tres constantes: MASK_DIA, MASK_MES y MASK_ANIO con el objetivo de "blanquear" los bits que no interesan y dejar "limpios" los bits que representan al día, mes y año respectivamente.

función getMes

El análisis es el mismo: debemos "blanquear" (poner en cero) todos aquellos bits que no representan al mes y dejar intactos los 4 bits nos interesan. La máscara para el mes está definida en la sección interface y corresponde al valor decimal 480.

|00000001|11100000|

La diferencia con la función anterior es que en este caso los bits que representan al mes no están ubicados a la derecha del word, están por el medio. Por esto, luego de aplicar la máscara tenemos que correrlos hacia la derecha para que su valor decimal coincida con el mes representado en la fecha.
 6:
7:function getMes(fecha:word):word;
8:var aux:word;
9:begin
10: aux:=fecha AND MASK_MES;
11: aux:=aux>>5;
12: getMes:=aux;
13:end;
14:

función getAnio

El análisis es el mismo que para getMes solo que además hay que considerar lo que se definió: 50 representa al año 2000, y año<50 o año>50 entonces representa al año 2000-50+año.
14:
15:function getAnio(fecha:word):word;
16:var aux:word;
17:begin
18: aux:=fecha AND MASK_ANIO;
19: aux:=aux>>9;
20: getAnio:=2000-50+aux;
21:end;
22:

procedimiento setDia

El procedimiento recibe la variable d que representa el día que tenemos que setear en la fecha. Entonces le aplicamos la máscara de día (por las dudas) y luego operamos la fecha y el día con el operador OR. Así asignaremos los bits indicados en d sin alterar los bits contenidos en fecha.
18:
19:procedure setDia(var fecha:word; d:word);
20:var aux:word;
21:begin
22: aux:=d AND MASK_DIA;
23: fecha:=fecha OR aux;
24:end;
25:

procedimiento setMes

El mismo análisis: primero corremos los bits 5 posiciones a la izquierda, luego le aplicamos la máscara de mes y por último aplicamos el OR.
10:
11:procedure setMes(var fecha:word; m:word);
12:var aux:word;
13:begin
14: aux:=m<<5;
15: aux:=aux AND MASK_MES;
16: fecha:=fecha OR aux;
17:end;
18:

procedimiento setAnio

Idem anterior pero primero "decodificamos" el año aplicando la fórmula a-2000+50.
 1:
2:procedure setAnio(var fecha:word; a:word);
3:var aux:word;
4:begin
5: aux:=a-2000+50;
6: aux:=aux<<9;
7: aux:=aux AND MASK_ANIO;
8: fecha:=fecha OR aux;
9:end;
10:









.