Capítulo 11 - Bases Numéricas y Operadores Lógicos Para Manejo de Bits.pdf
Document Details
Uploaded by Deleted User
Full Transcript
Capítulo 11 Bases Numéricas y Operadores Lógicos para Manejo de Bits Una computadora digital es una herramienta diseñada para resolver problemas que involucran el manejo de información, la cual puede ser de numérica o texto. Sin embargo, aún la información en forma de texto debe codificarse...
Capítulo 11 Bases Numéricas y Operadores Lógicos para Manejo de Bits Una computadora digital es una herramienta diseñada para resolver problemas que involucran el manejo de información, la cual puede ser de numérica o texto. Sin embargo, aún la información en forma de texto debe codificarse en forma de números a fin de que pueda ser procesada por la computadora. El entender cómo una computadora codifica la información y qué operaciones puede realizar con ella es importante ya que nos permite conocer las limitaciones que la computadora o el lenguaje nos impone en el manejo de la información, además de permitirnos seleccionar la forma óptima de almacenar la información en la computadora. Sistemas Numéricos Posicionales Un sistema numérico posicional es un método de codificar un número mediante una secuencia de n símbolos o dígitos en el cual cada dígito tiene un peso (está multiplicado por un factor) de acuerdo a su posición dentro de la secuencia. Asociado a cada sistema numérico tenemos una base r. Cada dígito tiene un valor entero en el rango de 0 a r-1. Números Enteros Considere el número entero N codificado por la secuencia: N = dn-1dn-2... d1d0 El valor de N está dado por: N = dn-1rn-1 + dn-2rn-2 +... + d1r1 + d0r0 Note que el peso de cada dígito, es una potencia de la base del sistema. El exponente de dicha potencia es la posición del dígito medido desde la derecha y empezando en cero. ITSON Manuel Domitsu Kono Capítulo 11 Bases Numéricas y Operadores Lógicos para Manejo de Bits 177 Sistema Decimal El sistema decimal que usamos cotidianamente es un sistema numérico posicional cuya base es el número 10 y cada dígito en ese sistema tiene un valor entero en el rango de 0 a 9, esto es existen diez dígitos diferentes con los que podemos construir un número. Los primeros diez números se representan con esos dígitos y son: 0 1 2 3 4 5 6 7 8 9 Para representar el siguiente número del 9 se requiere combinar dos de los dígitos anteriores. De hecho, los siguientes noventa números se forman con una combinación de dos dígitos: 10 11 12... 19 20 21 22... 29... 90 91 92... 99 El siguiente del 99, el 100 requiere de combinar tres dígitos, etc. El número entero N en el sistema decimal, codificado por la secuencia: N = dn-1dn-2... d1d0 tiene el valor dado por: N = dn-110n-1 + dn-210n-2 +... + d1101 + d0100 Por ejemplo, el número dado por la secuencia: 9354 está dado por: 9103 + 3102 + 5101 + 4100 es decir 9 unidades de millar + 3 centenas + 5 decenas y 4 unidades. Hay que recordar que unidades de millar significan grupos de mil, centenas significa grupos de 100 y decenas grupos de 10. Ahora, supongamos que tenemos un número de objetos, el cual deseamos representar en el sistema decimal, esto es, deseamos saber cuántas unidades, decenas, centenas, unidades de millar, etc. contiene ese número. Para determinar el número de unidades de ese número de objetos podemos agrupar los objetos en grupos de 10 (dividimos entre 10). Si al efectuar esta operación queda un residuo, este residuo constituye las unidades. Para obtener el número de decenas en el número tomamos el cociente de la operación anterior ITSON Manuel Domitsu Kono 178 Bases Numéricas y Operadores Lógicos para Manejo de Bits y lo dividimos entre 10. Si hay residuo este será las decenas. Este proceso se repite para obtener las centenas, unidades de millar. etc. Por ejemplo, si el número de objetos fuera 5316 al dividirlo entre 10 nos da un cociente de 531 y un residuo de 6. Luego el número de unidades en 5316 es 6. Dividiendo el 531 anterior entre 10 nos da 53 como cociente y 1 como residuo. Luego el número de decenas en 5316 es 1. Al dividir 53 entre 10 nos da como cociente 5 y residuo 3. El número de centenas es 3. Como 5 no es divisible entre 10 es el número de unidades de millar en 5316. El procedimiento anterior puede mecanizarse del siguiente modo: Número 5316 6 Residuos Cocientes 531 1 53 3 5 5 0 La lista de residuos escrita de abajo hacia arriba constituye la secuencia de dígitos que representa el número. Sistema Binario El sistema binario es otro sistema numérico posicional cuya base es el número 2 y cada dígito en ese sistema tiene un valor entero en el rango de 0 a 1, esto es, sólo existen dos dígitos diferentes con los que podemos construir un número, el 0 y el 1. El hecho de que el sistema binario sólo requiere de dos dígitos para representar cualquier número lo hace el sistema más convencional y fácil de implementar internamente en la computadora, ya que los ceros y unos se pueden representar mediante dos valores bien definidos de alguna variable de los circuitos de la computadora: voltaje, corriente, campo magnético, etc. Cada uno de los dígitos binarios recibe el nombre de bit (la palabra proviene de binary digit o dígito binario). Los primeros 16 números binarios con sus equivalentes decimales se muestran en la tabla 11-1. Note que los primeros dos números requieren de sólo un bit para representarse, los dos siguientes requieren de dos bits, los cuatro siguientes de tres, los siguientes ocho de cuatro, etc. El número entero N en el sistema binario, codificado por la secuencia: N = bn-1bn-2... b1b0 tiene el valor dado por: N = bn-12n-1 + bn-22n-2 +... + b121 + b020 Por ejemplo, el número dado por la secuencia: 101102 ITSON Manuel Domitsu Kono Capítulo 11 Bases Numéricas y Operadores Lógicos para Manejo de Bits 179 Tabla 11-1. Números decimales y sus equivalentes binarios y hexadecimales. Decimal Binario Hexadecimal 0 0 0 1 1 1 2 10 2 3 11 3 4 100 4 5 101 5 6 110 6 7 111 7 8 1000 8 9 1001 9 10 1010 A 11 1011 B 12 1100 C 13 1101 D 14 1110 E 15 1111 F 16 10000 10 está dado por: 124 + 023 + 122 + 121 + 020 es decir 1 grupo de 16 + 0 grupos de 8 + 1 grupo de cuatro + 1 grupo de 2 + 0 unidades. Los grupos de cuatro, ocho, 16 y 32 reciben los nombres de nibble, byte, word y double word. Note que, en el número utilizado en el ejemplo, 101102, se utiliza el subíndice 2 para denotar que ese número está en el sistema binario. El procedimiento para convertir un número binario a su equivalente decimal consiste en multiplicar cada dígito de la secuencia binaria por su peso y sumar los términos obtenidos, así: 101102 = 124 + 023 + 122 + 121 + 020 = 22 Ahora, supongamos que tenemos un número decimal, el cual deseamos representar en el sistema binario, esto es, deseamos saber cuántas unidades, grupos de dos, de cuatro, ocho, dieciséis, etc. contiene ese número. Podemos seguir un procedimiento similar al ya visto para el sistema decimal. Para determinar el número de unidades de ese número formamos grupos de 2 (dividimos por 2). Si al efectuar esta operación queda un residuo, este residuo constituye las unidades. Para obtener el número de grupos de 2 en el número tomamos el cociente de la operación anterior y lo dividimos entre 2. Si hay residuo este será el número de grupos de 2. Este proceso se repite para obtener los grupos de 4, ocho, dieciséis, etc. Por ejemplo, si el número de objetos fuera 53 al dividirlo entre 2 nos da un cociente de 26 y un residuo de 1. Luego el número de unidades en 53 es 1. Dividiendo el 26 entre 2 nos da 13 como cociente y 0 como residuo. Luego el número de grupos de dos en 53 es 0. Al ITSON Manuel Domitsu Kono 180 Bases Numéricas y Operadores Lógicos para Manejo de Bits dividir 13 entre 2 nos da como cociente 6 y residuo 1. El número de grupos de 4 es 1. Al dividir 6 entre 2 obtenemos 3 como cociente y 0 como residuo. El número de grupos de ocho es 0. Dividiendo 3 entre 2 nos da 1 como cociente y uno como residuo. El número de grupos de 16 es 1. Como el último cociente, 1, no es divisible entre 2 éste es el número de grupos de 32. El procedimiento anterior puede mecanizarse del siguiente modo: Número 53 1 Residuos Cocientes 26 0 13 1 6 0 3 1 1 1 0 La lista de residuos escrita de abajo hacia arriba constituye la secuencia de dígitos que representa el número. Por lo tanto: 53 = 1101012 Sistema Hexadecimal El sistema hexadecimal es otro sistema numérico posicional cuya base es el número 16 y cada dígito en ese sistema tiene un valor entero en el rango de 0 a 15, esto es, existen 16 dígitos diferentes con los que podemos construir un número, del 0 al 9 para los primeros 10 dígitos y las letras A a F para los siguientes 6 dígitos. Los primeros 16 números hexadecimales con sus equivalentes decimales y binarios se muestran en la tabla 11-1. Note que el número 16 requiere de dos dígitos hexadecimales para su representación. El número entero N en el sistema hexadecimal, codificado por la secuencia: N = hn-1hn-2... h1h0 tiene el valor dado por: N = hn-116n-1 + hn-216n-2 +... + h1161 + h0160 Por ejemplo, el número dado por la secuencia: 3B7F16 está dado por: 3163 + 11162 + 7161 + 15160 es decir 3 grupos de 4096 + 11 grupos de 256 + 7 grupos de 16 + 15 unidades. Note se han substituido los dígitos hexadecimales B y F por sus equivalentes decimales. Los números hexadecimales se representan con el subíndice 16 o también se les pone el prefijo 0x, que es la notación empleada en el lenguaje C: ITSON Manuel Domitsu Kono Capítulo 11 Bases Numéricas y Operadores Lógicos para Manejo de Bits 181 3B7F16 = 0x3B7F El procedimiento para convertir un número hexadecimal a su equivalente decimal consiste en multiplicar cada dígito de la secuencia hexadecimal por su peso y sumar los términos obtenidos, así: 3163 + 11162 + 7161 + 15160 = 15231 Ahora, supongamos que tenemos un número decimal, el cual deseamos representar en el sistema hexadecimal, esto es, deseamos saber cuántas unidades, grupos de 16, de 256, 4096, etc. contiene ese número. El procedimiento es similar al ya visto para el sistema binario. Para determinar el número de unidades de ese número formamos grupos de 16 (dividimos por 16). Si al efectuar esta operación queda un residuo, este residuo constituye las unidades. Para obtener el número de grupos de 16 en el número tomamos el cociente de la operación anterior y lo dividimos entre 16. Si hay residuo este será el número de grupos de 16. Este proceso se repite para obtener los grupos de 256, 4096, etc. Por ejemplo, si el número de objetos fuera 15482 al dividirlo entre 16 nos da un cociente de 967 y un residuo de 10 o A. Luego el número de unidades en 15482 es A. Dividiendo el 967 entre 16 nos da 60 como cociente y 7 como residuo. Luego el número de grupos de 16 en 15482 es 7. Al dividir 60 entre 16 nos da como cociente 3 y residuo 12 o C. El número de grupos de 256 es C. este último cociente, 3, no es divisible entre 16 por lo que éste es el número de grupos de 4096. El procedimiento anterior puede mecanizarse del siguiente modo: Número 15482 10=A Residuos Cocientes 967 7 60 12=C 3 3 0 La lista de residuos escrita de abajo hacia arriba constituye la secuencia de dígitos que representa el número. Por lo tanto: 15482 = 3C7A16 = 0x3C7A El sistema decimal es el sistema numérico que utilizamos cotidianamente, mientras que el sistema binario es el sistema numérico que por lo general emplean las computadoras digitales internamente. ¿Cuál es el uso del sistema hexadecimal? Una de las características del sistema binario es que aún para pequeños números decimales, su equivalente binario es una secuencia grande de bits. La manipulación de estos números no presenta problemas para la computadora, sin embargo, para los programadores el manejo de esas largas secuencias de bits representa una tarea tediosa y en la que es fácil equivocarse. Como veremos a continuación, el sistema hexadecimal constituye una notación abreviada de la notación binaria. Considere el siguiente número binario: N = b4(n-1)+3b4(n-1)+2b4(n-1)+1b4(n-1)b4(n-2)+3b4(n-2)+2b4(n-2)+1b4(n-2)...b7b6b5b4b3b2b1b0 ITSON Manuel Domitsu Kono 182 Bases Numéricas y Operadores Lógicos para Manejo de Bits y cuyo valor es: N = b4(n-1)+324(n-1)+3 + b4(n-1)+224(n-1)+2 + b4(n-1)+124(n-1)+1 + b4(n-1)24(n-1) + b4(n-2)+324(n-2)+3 + b4(n-2)+224(n-2)+2 + b4(n-2)+124(n-2)+1 + b4(n-2)24(n-2) +... + b727 + b626 + b525 + b424 + b323 + b222 + b121 + b020 Si agrupamos de derecha a izquierda de cuatro en cuatro bits, y si factorizamos esos cuatro bits por su máximo común divisor, tenemos: N = (b4(n-1)+323 + b4(n-1)+222 + b4(n-1)+121 + b4(n-1)20)24(n-1) + (b4(n-2)+323 + b4(n-2)+222 + b4(n-2)+121 + b4(n-2)20)24(n-2) +... + (b723 + b622 + b521 + b420)24 + (b323 + b222 + b121 + b020)20 Ahora, cualquier secuencia de 4 bits: bi+323 + bi+222 + bi+121 + bi20 corresponde a un número entre el cero y el 15, tal como lo podemos ver en la tabla 12-1, que podemos representar por el dígito decimal hi: hi = bi+323 + bi+222 + bi+121 + bi20 También el factor: 24i = (24)i = 16i por lo que el número binario N, puede expresarse como: N = hn-116n-1 + hn-216n-2 +... + h1161 + h0160 que corresponde al número hexadecimal: N = hn-1hn-2... h1h0 Del desarrollo anterior podemos ver que la conversión binaria a hexadecimal consiste en agrupar los bits del número binario en grupos de 4 de derecha a izquierda y convertir cada secuencia de cuatro bits a su equivalente hexadecimal. La conversión inversa consiste en expresar cada dígito hexadecimal como su equivalente binario. Dado que los números hexadecimales se representan como secuencias de dígitos cuatro veces más cortas que sus equivalentes binarios, es más fácil para el programador manipular los números hexadecimales que los binarios. Por otro lado, visualizar el patrón de bits de un número hexadecimal requiere de sólo visualizar cada dígito hexadecimal como una secuencia de cuatro bits. ITSON Manuel Domitsu Kono Capítulo 11 Bases Numéricas y Operadores Lógicos para Manejo de Bits 183 Por ejemplo, considere el siguiente número binario: 1011010111000102 al separarse en grupos de 4 bits nos da: 0101,1010,1110,0010 Note que se pueden agregar ceros a la izquierda del número para hacer que el número de bits sea un múltiplo de 4. Convirtiendo cada grupo de cuatro bits a su equivalente hexadecimal tenemos: 0101,1010,1110,0010 5 A E 2 Luego, 1011010111000102 = 5AE216 Como ejemplo de la operación de conversión hexadecimal a binario, tomemos el número hexadecimal: 3F5C16 Convirtiendo cada dígito hexadecimal a su equivalente binario tenemos: 3 F 5 C 11,1111,0101,1100 Luego 3F5C16 = 111111010111002 Nota: En el lenguaje C los números hexadecimales se escriben con el prefijo 0x en lugar del subíndice 16. Ejercicios 1. Convierte los siguientes números enteros decimales a binarios y hexadecimales: a) 43 b) 1394 2. Convierte los siguientes números enteros binarios a decimales y hexadecimales: a) 0110,10012 b) 1011,0000,0011,01012 3. Convierte los siguientes números enteros hexadecimales a binarios y decimales: a) 0x2B7D ITSON Manuel Domitsu Kono 184 Bases Numéricas y Operadores Lógicos para Manejo de Bits b) 0x4C3 Números Fraccionales En el subtema anterior se describió la manera de codificar un número entero mediante un sistema numérico posicional. El método anterior puede extenderse para incluir los números fraccionarios. Considere el número fraccionario F codificado por la secuencia: F = 0.d-1d-2... d-(m-1)d-m El valor de F está dado por: F = d-1r-1 + d-2r-2 +... + d-(m-1)r-(m-1) + d-mr-m Note que el peso de cada dígito, es una potencia negativa de la base del sistema. El exponente de dicha potencia es la posición del dígito medido desde la izquierda del punto. Sistema Decimal El número fraccionario F en el sistema decimal, codificado por la secuencia: F = 0.d-1d-2... d-(m-1)d-m tiene el valor dado por: F = d-110-1 + d-210-2 +... + d-(m-1)10-(m-1) + d-m10-m Por ejemplo, el número dado por la secuencia: 0.3954 está dado por: 310-1 + 910-2 + 510-3 + 410-4 es decir 3 décimos + 9 centésimos + 5 milésimos + 4 diezmilésimos. Ahora, supongamos que tenemos un número fraccionario, el cual deseamos representar en el sistema decimal, esto es, deseamos saber cuántos décimos, centésimos, milésimos, etc. contiene ese número. Para determinar el número de décimos en el número podemos multiplicarlo por 10. La parte entera del producto es el número de décimos. Para obtener el número de centésimos tomamos la parte fracciona del producto anterior y lo multiplicamos por 10. La parte entera del producto resultante es el número de centésimas. Este proceso se repite para obtener las centenas, unidades de millar. etc. Por ejemplo, si el número fraccionario es 0.165 al multiplicarlo por 10 nos da 1.65. Como la parte entera es 1, el número de décimos en 0.165 es 1. Tomando la parte fraccionaria del ITSON Manuel Domitsu Kono Capítulo 11 Bases Numéricas y Operadores Lógicos para Manejo de Bits 185 producto 0.65 y la multiplicamos por 10 nos da 6.5, por lo que el número de centésimas es 6. Multiplicando la parte fraccionaria 0.5 por 10 nos da 5. Luego el número de milésimas es 5. El procedimiento anterior puede mecanizarse del siguiente modo: Número 0.165 Parte Fraccionaria Parte Entera 1.65 6.5 5.0 La lista de los números enteros resultantes de las multiplicaciones por 10, leídas de arriba hacia abajo constituye la secuencia de dígitos que representa el número. No todos los números fraccionarios pueden representarse exactamente mediante una secuencia finita de dígitos. Por ejemplo, la fracción: 1/3 = 0.333333 requiere de una secuencia infinita para representarse. Lo mismo sucede para las fracciones 1/6, 1/9, 2/3, π/10, etc. Sistema Binario El número fraccionario F en el sistema binario, codificado por la secuencia: F = 0.b-1b-2... b-(m-1)b-m tiene el valor dado por: F = b-12-1 + b-22-2 +... + b-(m-1)2-(m-1) + b-m2-m Por ejemplo, el número fraccionario dado por la secuencia: 0.100112 está dado por: 12-1 + 02-2 + 02-3 + 12-4 + 12-5 es decir 1/2 + 1/16 + 1/32 = 0.5 + 0.0625 + 0.03125 = 0.59375 luego, el procedimiento para convertir un número fraccionario binario a su equivalente decimal consiste en multiplicar cada dígito de la secuencia binaria por su peso y sumar los términos obtenidos. Ahora, supongamos que tenemos un número fraccionario decimal, el cual deseamos representar en el sistema binario, esto es, deseamos saber cuántos medios, cuartos, octavos, etc. contiene ese número. Podemos seguir un procedimiento similar al ya visto ITSON Manuel Domitsu Kono 186 Bases Numéricas y Operadores Lógicos para Manejo de Bits para el sistema decimal. Para determinar el número de medios de ese número multiplicaremos el número fraccionario por 2 y la parte entera resultante constituye el número de medios en el número dado. Multiplicamos de nuevo la parte fraccionaria del producto anterior por dos. La parte entera del número resultante nos da el número de cuartos. Este proceso se repite para obtener los octavos, dieciseisavos, etc. Por ejemplo, si el número decimal fraccionario es 0.3125. Al multiplicarlo por 2 obtenemos 0.625. Como la parte entera del producto resultante es 0, el número de medios en 0.3125 es 0. Multiplicando la parte fraccionaria del producto anterior por 2 nos da 1.25 y como la parte entera es 1, el número de cuartos en 0.3125 es uno. Multiplicando la parte fraccionaria del producto anterior por 2 da 0.5 y por lo tanto el número de octavos en 0.3125 es cero. Al multiplicar la parte fraccionaria del producto anterior obtenemos 1.0, por lo que el número de dieciseisavo en 0.3125 es 1 y como la parte fraccionaria es cero se termina la conversión del número. El procedimiento anterior puede mecanizarse del siguiente modo: Número 0.3125 Parte Fraccionaria Parte Entera 0.625 1.25 0.5 1.0 La lista de los números enteros resultantes de las multiplicaciones por 2, leídas de arriba hacia abajo constituye la secuencia de dígitos que representa el número, por lo que: 0.3125 = 0.01012 Así como no todas las fracciones pueden representarse en forma exacta mediante una secuencia de dígitos finitos en el sistema decimal, tampoco todas las fracciones pueden representarse en forma exacta en el sistema binario. Por ejemplo, considere la fracción 0.3. Al convertirla a binario, tenemos: Número 0.3 Parte Fraccionaria Parte Entera 0.6 1.2 0.4 0.8 1.6 1.2 0.4....... Podemos ver que la parte fraccionaria nunca se reduce a cero, y que además se repite, por lo que: 0.3 = 0.01001 Sistema Hexadecimal De la misma manera que con los sistemas decimal y binario, el número fraccionario F en el sistema hexadecimal, codificado por la secuencia: ITSON Manuel Domitsu Kono Capítulo 11 Bases Numéricas y Operadores Lógicos para Manejo de Bits 187 F = 0.h-1h-2... h-(m-1)h-m tiene el valor dado por: F = h-116-1 + h-216-2 +... + h-(m-1)16-(m-1) + h-m16-m Por ejemplo, el número fraccionario dado por la secuencia: 0.1F816 está dado por: 116-1 + 1516-2 + 816-3 es decir 1/16 + 15/256 + 8/4096 = 0.0625 + 0.05859375 + 0.001953125 lo que nos da: 0.123046875 luego, el procedimiento para convertir un número fraccionario hexadecimal a su equivalente decimal consiste en multiplicar cada dígito de la secuencia hexadecimal por su peso y sumar los términos obtenidos. Ahora, supongamos que deseamos realizar la conversión inversa, es decir, tenemos un número fraccionario decimal, el cual deseamos representar en el sistema hexadecimal. El procedimiento es similar a los ya vistos para los sistemas decimal y binario. Para determinar el número de dieciseisavos de ese número multiplicaremos el número fraccionario por 16 y la parte entera resultante constituye el número de dieciseisavos en el número dado. Multiplicamos de nuevo la parte fraccionaria del producto anterior por 16. La parte entera del número resultante nos da el número de doscientoscincuentaiseisavos. Este proceso se repite para obtener los demás dígitos. Por ejemplo, si el número decimal fraccionario es 0.15625. Al multiplicarlo por 16 obtenemos 2.5. Como la parte entera del producto resultante es 2, el número de dieciseisavos en 0.15625 es 2. Multiplicando la parte fraccionaria del producto anterior por 16 nos da 8.0 y como la parte entera es 8, el número de 1/256 en 0.15625 es 8 y como la parte fraccionaria es cero se termina la conversión del número. El procedimiento anterior puede mecanizarse del siguiente modo: Número 0.15625 Parte Fraccionaria Parte Entera 2.5 8.0 ITSON Manuel Domitsu Kono 188 Bases Numéricas y Operadores Lógicos para Manejo de Bits La lista de los números enteros resultantes de las multiplicaciones por 16, leídas de arriba hacia abajo constituye la secuencia de dígitos que representa el número, por lo que: 0.15625 = 0.2816 También como en el caso de los sistemas decimal y binario, en el sistema hexadecimal hay fracciones que no pueden representarse en forma exacta mediante una secuencia de dígitos finitos. Por ejemplo, considere la fracción 0.3. Al convertirla a hexadecimal, tenemos: Número 0.3 Parte Fraccionaria Parte Entera 4.8 C.8 C.8...... Podemos ver que la parte fraccionaria nunca se reduce a cero, y que además se repite, por lo que: 0.3 = 0.4C También al igual que con los números enteros, la conversión binaria a hexadecimal de números fraccionarios se puede hacer agrupando los bits del número binario en grupos de 4, pero en este caso de izquierda a derecha a partir del punto binario y convirtiendo cada secuencia de cuatro bits a su equivalente hexadecimal. La conversión inversa consiste en expresar cada dígito hexadecimal como su equivalente binario. Por ejemplo, considere el siguiente número binario: 0.10111011100112 al separarse en grupos de 4 bits nos da: 0.1011,1011,1001,1000 Note que se pueden agregar ceros a la derecha del número para hacer que el número de bits sea un múltiplo de 4. Convirtiendo cada grupo de cuatro bits a su equivalente hexadecimal tenemos: 0.1011,1011,1001,1000 0. B B 9 8 Luego, 0.10111011100112 = 0.BB9816 Como ejemplo de la operación de conversión hexadecimal a binario, tomemos el número hexadecimal: 0.4D5C16 ITSON Manuel Domitsu Kono Capítulo 11 Bases Numéricas y Operadores Lógicos para Manejo de Bits 189 Convirtiendo cada dígito hexadecimal a su equivalente binario tenemos: 0. 4 D 5 C 0.0100,1101,0101,1100 Luego 0.4D5C16 = 0.01001101010111002 = 0.010011010101112 Ejercicios 1. Convierte los siguientes números fraccionarios decimales a binarios y hexadecimales: a) 0.3245 b) 0.9372 2. Convierte los siguientes números fraccionarios binarios a decimales y hexadecimales: a) 0.1110,01112 b) 0.1011,00002 3. Convierte los siguientes números fraccionarios hexadecimales a binarios y decimales: a) 0.17B16 b) 0.49E16 Números Reales Un número real es aquel que tiene una parte entera y una fraccionaria y por lo tanto puede codificarse en un sistema numérico posicional. Hay dos formas de codificar un número real: Notación de punto fijo y Notación de punto flotante. Notación de Punto Fijo Considere el número real R codificado por la secuencia: R = dn-1dn-2... d1d0.d-1d-2... d-(m-1)d-m En este caso se dice que el número está en notación de punto fijo y su valor está dado por: R = dn-1rn-1 + dn-2rn-2 +... + d1r1 + d0r0 + d-1r-1 + d-2r-2 +... + d-(m-1)r-(m-1) + d-mr-m donde r es la base del sistema en el que se está codificando el número. En el sistema decimal, un número real en notación de punto fijo, estaría codificado por la secuencia: ITSON Manuel Domitsu Kono 190 Bases Numéricas y Operadores Lógicos para Manejo de Bits R = dn-1dn-2... d1d0.d-1d-2... d-(m-1)d-m y tiene el valor dado por: R = dn-110n-1 + dn-210n-2 +... + d1101 + d0100 + d-110-1 + d-210-2 +... + d-(m-1)10-(m-1) + d-m10-m En el sistema binario, un número real en notación de punto fijo, estaría codificado por la secuencia: R = bn-1bn-2... b1b0.b-1b-2... b-(m-1)b-m y tiene el valor dado por: R = bn-12n-1 + bn-22n-2 +... + b121 + b020 + b-12-1 + b-22-2 +... + b-(m-1)2-(m-1) + b-m2-m Por último en el sistema hexadecimal, un número real en notación de punto fijo, estaría codificado por la secuencia: R = hn-1hn-2... h1h0.h-1h-2... h-(m-1)h-m y tiene el valor dado por: R = hn-116n-1 + hn-216n-2 +... + h1161 + h0160 + h-116-1 + h-216-2 +... + h-(m-1)16-(m-1) + h-m16-m Las conversiones de una base a otra se realizan convirtiendo por separado la parte entera y la parte fraccionaria, utilizando los métodos descritos en los subtemas anteriores. Notación de Punto Flotante Sea R un número real codificado en la notación de punto fijo por la secuencia: R = dn-1dn-2... d1d0.d-1d-2... d-(m-1)d-m y cuyo valor está dado por: R = dn-1rn-1 + dn-2rn-2 +... + d1r1 + d0r0 + d-1r-1 + d-2r-2 +... + d-(m-1)r-(m-1) + d-mr-m Si factorizamos R por rn-1, tenemos: R = rn-1(dn-1r0 + dn-2r-1 +... + d1r-(n-2) + d0r-(n-1) + d-1r-n + d-2r-(n+1) +... + d-(m-1)r-(n+m-2) + d-mr-(n+m-1)) o R = dn-1.dn-2... d1d0d-1d-2... d-(m-1)d-mrn-1 ITSON Manuel Domitsu Kono Capítulo 11 Bases Numéricas y Operadores Lógicos para Manejo de Bits 191 Aquí se dice que el número está codificado en la notación de punto flotante. Note que en esta notación hay un solo dígito a la izquierda del punto y que el exponente del factor rn-1 representa el número de posiciones que se recorrió el punto, si es positivo hacia la izquierda y si es negativo hacia la derecha. En el sistema decimal, el número real en notación de punto fijo: R = dn-1dn-2... d1d0.d-1d-2... d-(m-1)d-m se representa en notación flotante por: R = dn-1.dn-2... d1d0d-1d-2... d-(m-1)d-m10n-1 Por ejemplo: 6375.56981 = 6.37556981 103 0.0009743 = 9.743 10-4 En el sistema binario, el número real en notación de punto fijo: R = bn-1bn-2... b1b0.b-1b-2... b-(m-1)b-m tiene como representación de punto flotante a: R = bn-1.bn-2... b1b0b-1b-2... b-(m-1)b-m2n-1 Por ejemplo: 100111.10001101 = 1.001111000110125 0.000001100101 = 1.1001012-6 Por último, en el sistema hexadecimal, el número real en notación de punto fijo: R = hn-1hn-2... h1h0.h-1h-2... h-(m-1)h-m se escribe en notación de punto flotante como: R = hn-1.hn-2... h1h0h-1h-2... h-(m-1)h-m16n-1 Por ejemplo: 3A10.F2C45 = 3.A10F2C45163 0.05B50 = 5.B5016-2 Ejercicios 1. Convierte los siguientes números reales decimales a binarios y hexadecimales, expresándolos tanto en su notación de punto fijo como flotante: ITSON Manuel Domitsu Kono 192 Bases Numéricas y Operadores Lógicos para Manejo de Bits a) 132.3245 b) 1.937210-2 2. Convierte los siguientes números fraccionarios binarios a decimales, expresándolos tanto en su notación de punto fijo como flotante: a) 110.1110,01112 b) 1.1011,0000223 3. Convierte los siguientes números fraccionarios hexadecimales a decimales, expresándolos tanto en su notación de punto fijo como flotante: a) A.17B1616-2 b) F0.49E16 Operaciones Aritméticas Las operaciones aritméticas con números en base r siguen las mismas reglas que los números decimales. Sólo se tiene que tener cuidado al emplear otras bases diferentes a la base 10, de emplear sólo los r dígitos permitidos en esa base y de realizar todos los cálculos con los dígitos de la base r. En este tema sólo veremos las operaciones de suma y resta en el sistema binario. Esto se debe a que no estamos realmente interesados en aprender a efectuar todas las operaciones que la computadora realiza con mayor eficiencia. Nuestro interés por las operaciones antes mencionadas es el que nos va a ayudar a comprender cuáles son las limitaciones que tienen las operaciones dentro de la máquina y qué pasa cuando se rebasan dichas limitaciones. Suma y Resta de Números Enteros El proceso de sumar o restar dos enteros expresados en binario es similar al proceso empleado con dos números en base 10. Es más, es un proceso más sencillo ya que la tabla de sumar es más sencilla, como se muestra en la tabla 11-2. Tabla 11-2. Tabla de sumar binaria + 0 1 0 0 1 1 1 10 ITSON Manuel Domitsu Kono Capítulo 11 Bases Numéricas y Operadores Lógicos para Manejo de Bits 193 Note en la tabla que al sumar 1 + 1 obtenemos 10, esto es se deja un 0 y se tiene un acarreo de 1. Por ejemplo, considere la siguiente suma: 111 Acarreos 11010100 Sumando + 10001100 Sumando ────────── 101100000 Suma Ahora hagamos la siguiente resta 11010100 Minuendo - 10001100 Substraendo ────────── 01001000 Diferencia Ejercicios Efectúa las siguientes operaciones indicadas a) 11001112 + 01010002 b) 11001112 - 01010002 Complementos Otras dos operaciones realizadas por la computadora y que es necesario conocer son las operaciones de complemento. Hay dos clases de complemento en cada sistema de base r: El complemento de r - 1 y el complemento de r. Complemento de r - 1, Cr-1 Dado un número positivo N en base r con parte entera de n dígitos y parte fraccionaria de m dígitos, se define el complemento de r - 1 de N, Cr-1(N) como rn - r-m - N para N 0 y 0 para N = 0. En el caso del sistema decimal el complemento de r - 1 de N, Cr-1(N), se llama complemento de 9 de N, C9(N), y en el caso del sistema binario se conoce como el complemento de 1, C1(N). Por ejemplo: C9(25310) = 105 - 10-0 - 25310 = 100000 - 1 - 25310 = 99999 - 25310 = 74689 C9(0.312) = 100 - 10-3 - 0.312 = 1 - 0.001 - 0.312 = 0.999 - 0.312 = 0.687 C9(12.35) = 102 - 10-2 - 12.35 = 100 - 0.01 - 12.35 = 99.99 - 12.35 = 87.64 C1(1011) = 24 - 2-0 - 10112 = 100002 - 12 - 10112 ITSON Manuel Domitsu Kono 194 Bases Numéricas y Operadores Lógicos para Manejo de Bits = 11112 - 10112 = 01002 C1(1.01) = 21 - 2-2 - 1.012 = 102 - 0.012 - 1.012 = 1.112 - 1.012 = 0.102 Podemos ver que el complemento de 9 de un número decimal se forma substrayendo cada dígito de 9, mientras que el complemento de 1 de un número binario se obtiene cambiando los ceros del número por unos y los unos por ceros. Una propiedad de la operación de complemento de r - 1, es la de que si sacamos el complemento del complemento de un número nos regresa al valor original. Cr-1(Cr-1(N)) = Cr-1(rn - r-m - N) = rn - r-m - (rn - r-m - N) = N Complemento de r Dado un número positivo N en base r con parte entera de n dígitos, se define el complemento r de N, Cr(N), como rn - N para n 0 y 0 para N = 0. En el caso del sistema decimal el complemento de r de N, Cr(N), se llama complemento de 10 de N, C10(N), y en el caso del sistema binario se conoce como el complemento de 2 de N, C2(N). Por ejemplo: C10(25310) = 105 - 25310 = 100000 - 25310 = 74690 C10(0.312) = 100 - 0.312 = 1 - 0.312 = 0.688 C10(12.35) = 102 - 12.35 = 100 - 12.35 = 87.65 C2(1011) = 24 - 10112 = 100002 - 10112 = 01012 C2(1.01) = 21 - 1.012 = 102 - 1.012 = 0.112 Podemos ver que el complemento de 10 de un número decimal se forma dejando los ceros menos significativos inalterados, restando el primer dígito menos significativo diferente de 0 de 10 y luego sustraer el resto de los dígitos más significativos de 9. El complemento de 2 de un número binario se obtiene dejando todos los ceros menos significativos y el primer bit menos significativo diferente de cero inalterados y luego reemplazar unos por ceros y ceros por unos en el resto de los bits más significativos. Observando de las definiciones de complemento de r y complemento de r - 1 podemos ver que el complemento a r de un número se obtiene sumándole al complemento de r - 1 del número r-m. Esto equivale a sumarle un uno al dígito menos significativo del complemento de r - 1. ITSON Manuel Domitsu Kono Capítulo 11 Bases Numéricas y Operadores Lógicos para Manejo de Bits 195 Por ejemplo: Número 12.35 Complemento de 9 87.64 +1 ───── Complemento de 10 87.65 Número 10101.101 Complemento de 1 01010.010 +1 ───────── Complemento de 2 01010.011 Una propiedad de la operación de complemento de r, es la de que si sacamos el complemento del complemento de un número nos regresa al valor original. Cr(Cr(N)) = Cr(rn - N) = rn - (rn - N) = N Ejercicios 1. Encuentre los complementos de 9 y de 10 de los siguientes números decimales a) 53710 b) 90.312 2. Encuentre los complementos de 1 y de 2 de los siguientes números binarios a) 1010112 b) 10.112 Substracción con Complementos de r La principal aplicación de la operación de complemento a r de un número es implementar un método para realizar la operación de substracción en forma eficiente para las computadoras. El algoritmo para restar dos números positivos (M - N) en base r es el siguiente: 1. Expresar ambos números M y N con el mismo número de dígitos, agregando ceros a la izquierda del dígito más significativo o la derecha del dígito menos significativo según sea necesario. 2. Calcule el complemento a r del substraendo N: rn - N 3. Súmese al minuendo el complemento del substraendo: M + (rn - N) ITSON Manuel Domitsu Kono 196 Bases Numéricas y Operadores Lógicos para Manejo de Bits 4. Si M > N, la suma nos da: (M - N) + rn El término rn constituye un acarreo final. Si lo desechamos lo que queda es el resultado M - N. 5. Si M < N, la suma no genera un acarreo final y nos da: rn - (N - M) Note que éste es el complemento a r de (N - M). Complementando este resultado obtenemos: N-M Por último, multiplicando este valor por -1 obtenemos el resultado esperado: M-N Ejemplos: 1. Use el complemento de 10 para substraer 87540 - 3467 M = 87540 N = 03467 Complemento de 10 de N N = 03467 C9 (N) = 96532 C10(N) = 96533 Sumando M y el complemento de 10 de N 87540 + 96533 ────── 184073 En la operación anterior se genera un acarreo final. Desechándolo obtenemos el resultado: 87540 - 03467 = 84073 2. Substraiga 3467 - 87540 usando el complemento de 10 M = 03467 ITSON Manuel Domitsu Kono Capítulo 11 Bases Numéricas y Operadores Lógicos para Manejo de Bits 197 N = 87540 Complemento de 10 de N N = 87540 C9 (N) = 12459 C10(N) = 12460 Sumando M y el complemento de 10 de N 03467 + 12460 ───── 15927 En la operación anterior no se genera un acarreo final, por lo que el resultado anterior es el complemento de 10 de (N - M). Complementando de 10: 105 - (N - M) = 15927 C9 (105 - (N - M)) = 84072 C10(105 - (N - M)) = 84073 Multiplicando la cantidad por -1 obtenemos (M - N) 3467 - 87540 = -84073 3. Use el complemento de 10 para substraer 37.534 - 8.36 M = 37.534 N = 08.360 Complemento de 10 de N N = 08.360 C9 (N) = 91.639 C10(N) = 91.640 Sumando M y el complemento de 10 de N 37.534 + 91.640 ───── 129.174 En la operación anterior se genera un acarreo final. desechándolo obtenemos el resultado: 37.534 - 8.36 = 29.174 ITSON Manuel Domitsu Kono 198 Bases Numéricas y Operadores Lógicos para Manejo de Bits 4. Use el complemento de 2 para substraer 1100101 - 10011 M = 1100101 N = 0010011 Complemento de 2 de N N = 0010011 C1 (N) = 1101100 C2 (N) = 1101101 Sumando M y el complemento de 2 de N 1100101 + 1101101 ──────── 11010010 En la operación anterior se genera un acarreo final. desechándolo obtenemos el resultado: 1100101 - 10011 = 1010010 5. Substraiga 10011 - 1100101 usando el complemento de 2 M = 0010011 N = 1100101 Complemento de 2 de N N = 1100101 C1 (N) = 0011010 C2 (N) = 0011011 Sumando M y el complemento de 2 de N 0010011 + 0011011 ─────── 0101110 Como la operación anterior no se genera un acarreo final, el resultado anterior es el complemento de 2 de (N - M). Complementando de 2: 28 - (N - M) = 0101110 C1 (28 - (N - M)) = 1010001 C2 (28 - (N - M)) = 1010010 Multiplicando la cantidad por -1 obtenemos (M - N) 10011 - 1100101 = -1010010 ITSON Manuel Domitsu Kono Capítulo 11 Bases Numéricas y Operadores Lógicos para Manejo de Bits 199 6. Use el complemento de 2 para substraer 11.001 - 1.01 M = 11.001 N = 01.010 Complemento de 10 de N N = 01.010 C1 (N) = 10.101 C2 (N) = 10.110 Sumando M y el complemento de 2 de N 11.001 + 10.110 ────── 101.111 En la operación anterior se genera un acarreo final. desechándolo obtenemos el resultado: 11.001 - 1.01 = 1.111 Ejercicios 1. Efectúe las siguientes substracciones usando complemento de 10 a) 876 - 765 b) 45.69 - 123.73 2. Efectúe las siguientes substracciones usando complemento de 2 a) 1101011 - 100111 b) 1000.011 - 1000.11 Representación de Números Enteros en C Como ya se mencionó anteriormente, todos los números dentro de la computadora se almacenan en el sistema binario. Cada número se almacena en una localidad de la memoria de la computadora. Esas localidades de memoria corresponden a direcciones de dispositivos que nos permiten almacenar una secuencia de bits. El número de bits que contiene esa secuencia, determina el rango de valores que un número puede tomar. Con una secuencia de un sólo bit sólo podemos representar dos (21) valores, codificados como 0 y 1. Si la secuencia es de dos bits, el número de valores se incrementa a 4 (22), codificados como: 00, 01, 10, 11. Con tres bits podemos representar 8 (23) valores, ITSON Manuel Domitsu Kono 200 Bases Numéricas y Operadores Lógicos para Manejo de Bits codificados como: 000, 001, 010, 011, 100, 101, 110, 111. Podemos ver que, si disponemos de n bits, el número de valores que se pueden representar es de 2n. Típicamente, los tamaños de las secuencias de bits permitidas por un compilador de C, están determinadas por la arquitectura de la computadora en la que se van a ejecutar los programas hechos con ese compilador. Es una práctica casi universal que esos tamaños sean múltiplos de 8 bits. En el Capítulo 3: Tipo de datos y expresiones en C se vio que los tipos enteros básicos de C son: char e int. Al tipo int se le puede aplicar el calificador short, long o long long para producir enteros de otras longitudes: short int (o simplemente short), long int (o simplemente long) y long long int (o simplemente long long). El estándar de C sólo establece el tamaño del tipo char, los tamaños de los otros tipos enteros dependen de la implementación del compilador de C. Las únicas restricciones son las siguientes: El tipo char es siempre de un byte (8 bits). El tipo short es de al menos 16 bits. El tipo int es mayor o igual al tipo short. El tipo long es de al menos 32 bits. El tipo long es mayor o igual al tipo int. El tipo long long es de al menos 64 bits. El tipo long long es mayor o igual al tipo long. También se vio que a todos los tipos enteros anteriores se les puede aplicar los calificadores signed y unsigned. Los enteros unsigned son siempre positivos o cero. Los enteros signed pueden ser positivos, cero o negativos. Números Enteros Sin Signo En el caso de los enteros sin signo, si un número se almacena en una localidad de n bits, su rango va de 0 a 2n -1. En la tabla 11-3 se muestran los rangos que pueden tener los diferentes tipos enteros sin signo dependiendo de su tamaño en bits. Tabla 11-3. Rango de los tipos enteros sin signo Tipos (Dependiendo del Tamaño (Bits) Rango compilador) 8 0 - 28-1 unsigned char (0 - 255) 16 0 - 216-1 unsigned short / unsigned (0 - 65535) 32 0 - 232-1 unsigned / unsigned long (0 - 4294967295) 64 0 – 264-1 unsigned long long (0 - 18,446,744,073,709,551,615) ITSON Manuel Domitsu Kono Capítulo 11 Bases Numéricas y Operadores Lógicos para Manejo de Bits 201 Para determinar la representación de un entero sin signo dentro de una localidad de memoria tomamos el equivalente binario del número y le agregamos tantos ceros a la izquierda del número como sea necesario para que llenar la localidad de memoria. Por ejemplo el número 37 almacenado como unsigned char aparecería como: 37 = 0010,0101 = 0x25 El número 1,257 almacenado como un unsigned short (o unsigned, dependiendo del compilador) tendría la forma: 1,257 = 0000,0100 1110,1001 = 0x04E9 El número 125,000 almacenado como un unsigned (o unsigned long, dependiendo del compilador) tendría la forma: 125,000 = 0000,0000 0000,0001 1110,1000 0100,1000 = 0x0001E848 Números Enteros con Signo Además de los tipos enteros sin signo, C nos permite manejar enteros que puedan tomar tanto valores positivos como negativos. A este tipo de enteros se les conoce como enteros con signo. Una práctica común para almacenar el signo del número, es utilizar uno de los bits de la secuencia de bits que representa al número. Por lo general se utiliza el bit más significativo de la localidad de memoria. Si el número es positivo, el bit más significativo es cero y si el número es negativo 1. Podemos ver que si disponemos de n bits para representar al número y uno de ellos se reserva para el signo, el número de valores que se pueden representar es de 2n y que el rango es del -2n-1 a 2n-1- 1. El número de bits que ocupan los tipos enteros con signo corresponde al de su equivalente sin signo. En la tabla 11-4 se muestran los rangos que pueden tener los diferentes tipos enteros con signo dependiendo de su tamaño en bits. Tabla 11-4. Rango de los tipos Enteros con signo Tamaño Tipos (Dependiendo Rango (Bits) del compilador) 8 -27 - 27-1 signed char (-128 - 127) 16 -215 - 215-1 short /int (-32768 - 32767) 32 -231 - 231-1 int / long (-2147483648 - 2147483647) 64 -263 – 263-1 long long (-9,223,372,036,854,775,807 – 9,223,372,036,854,775,807) ITSON Manuel Domitsu Kono 202 Bases Numéricas y Operadores Lógicos para Manejo de Bits La representación de un entero con signo positivo es igual a la de un entero sin signo. Si la localidad de memoria es de n bits, el mayor número positivo permitido es de 2n-1-1 y éste sólo ocupa para su representación n-1 bits, entonces el bit más significativo siempre es cero, que corresponde precisamente con la convención de que el bit más significativo de un entero positivo es cero. La representación de un entero negativo, es el complemento de 2 de la representación en la misma localidad de memoria del valor absoluto del número. Sea i un número negativo que deseamos representar dentro de una localidad de memoria de n bits, por lo tanto, i está en el rango de -2n-1 i -1. La representación de ese número será el complemento de 2 de i, es decir 2n - i. Supongamos que representamos este complemento como: 2n - i = 2n-1 + j despejando el valor de j, tenemos: j = 2n - 2n-1 - i = 2n-1 (2 - 1)- i = 2n-1 - i por lo que j será un número positivo en el rango: 0 j 2n-1 - 1. El número 2n-1 es un uno seguido de n - 1 ceros, mientras que j es un número que cuando mucho ocupa n - 1 bits. Por lo tanto 2n - i = 2n-1 + j es un número con el bit más significativo igual a uno, que corresponde precisamente con la convención de que el bit más significativo de un entero negativo es uno. Por ejemplo el número -37 almacenado como un signed char 37 = 0010,0101 1101,1010 Complemento de 1 1101,1011 Complemento de 2 -37 = 1101,1011 = 0xDB El número -1,257 almacenado como un short (o int, dependiendo del compilador) tendría la forma: 1,257 = 0000,0100,1110,1001 1111,1011,0001,0110 Complemento de 1 1111,1011,0001,0111 Complemento de 2 -1,257 = 1111,1011 0001,0111 = 0xFB17 El número -125,000 almacenado como un int (o long, dependiendo del compilador) tendría la forma: ITSON Manuel Domitsu Kono Capítulo 11 Bases Numéricas y Operadores Lógicos para Manejo de Bits 203 125,000 = 0000,0000,0000,0001,1110,1000,0100,1000 1111,1111,1111,1110,0001,0111,1011,0111 Complemento de 1 1111,1111,1111,1110,0001,0111,1011,1000 Complemento de 2 -125,000 = 1111,1111 1111,1110 0001,0111 1011,1000 = 0xFFFE17B8 Ejercicios 1. Muestra el patrón de bits, como una secuencia hexadecimal, almacenados en las siguientes variables (Considere que los tipos unsigned e int son de 32 bits): a) unsigned char x = 185; b) char y = -95; c) unsigned z = 12348; d) int w = -549; 2. Encuentre el valor de las siguientes variables si su patrón de bits, como una secuencia hexadecimal, es el siguiente (Considere que los tipos unsigned e int son de 32 bits): a) unsigned char x; 0x81 b) char y; 0xAB c) unsigned z; 0xAD5C d) int w; 0xA17C Representación de Números Reales en C Los números reales o flotantes se representan en C en notación de punto flotante. Un número real en notación flotante en el sistema binario tiene la forma: F = m 2e donde m se llama la mantisa y e el exponente. La representación del número real en una localidad de memoria de n bits tiene la siguiente forma: 1 bit para el signo de la mantisa m r bits para el exponente e p bits para la mantisa, p = n - r - 1. Como ya se mencionó los tipos de números flotantes float y double de C siguen el formato del estándar IEEE 754, el cual establece los valores para los tamaños del exponente y la mantisa mostrados en la tabla 11-5. ITSON Manuel Domitsu Kono 204 Bases Numéricas y Operadores Lógicos para Manejo de Bits Tabla 11-5. Tamaño de exponente y mantisa de los tipos flotantes de acuerdo al estándar IEEE 754. Tipos Bits totales Bits en Exponente Bits en Mantisa float 32 8 23 double 64 11 52 Tanto la mantisa m como el exponente e tienen signo. A diferencia de los enteros con signo, la representación del signo en los números flotantes no utiliza el complemento de 2 para los números negativos ni en la mantisa ni en el exponente. El signo de la mantisa, que es el que determina el signo del número, utiliza el bit más significativo y vale 0 si el número es positivo y 1 si es negativo. El exponente está formado por una secuencia de r bits codificado en lo que se llama exponente polarizado. El exponente polarizado, eP, consiste de un valor en el rango 0 eP 2r - 1 y codifica el valor del exponente real de la manera como se muestra en la tabla 11-6. En esta tabla se puede ver que hay dos valores de exponente polarizado con significado especial: 0 y 2r - 1. Los otros exponentes polarizados se obtienen de sus correspondientes exponentes de la siguiente manera: epolarizado = e + 2r-1 - 1 El rango de los exponentes está dado por -(2r-1 - 2) e 2r-1 - 1 mientras que el rango de los exponentes polarizados queda como: 1 epolarizado 2r-2. En la tabla 11-7 se muestran los valores máximos y mínimos para los exponentes en los tipos flotantes. Tabla 11-6. Exponentes y exponentes polarizados. eP e Significado 0 - F=0 1 1 - (2r-1 - 1) = -(2r-1 - 2) 2 2 - (2r-1 - 1) = -(2r-1 - 3)...... 2r-1 – 2 2r-1 - 2 - (2r-1 - 1) = -1 2r-1 – 1 2r-1 - 1 - (2r-1 - 1) = 0 2r-1 2r-1 - (2r-1 - 1) =1...... 2r - 2 2r - 2 - (2r-1 - 1) = 2r-1 - 1 2r - 1 - F = , si m = 0.0 F = NAN, si m ≠0.0 ITSON Manuel Domitsu Kono Capítulo 11 Bases Numéricas y Operadores Lógicos para Manejo de Bits 205 Nam = resultado no es un número, por ejemplo 0.0/0.0 Tabla 11-7. Valores máximos y mínimos de los exponentes de los tipos flotante. Tipos eMínimo eMáximo Polarización eP Mínimo eP Máximo 2r-1 - 1 float -126 127 127 1 254 double -1022 1023 1023 1 2046 Dado que el número está en notación flotante en el sistema binario la mantisa tiene la forma: 1.b-1b-2b-3... Si esta secuencia de bits se almacena en los bits destinados a la mantisa se dice que el número no está normalizado. Note que siempre habrá un uno a la izquierda del punto. Entre mayor sea el número de bits que usen para la mantisa, mayor es la precisión en la representación del número, entonces podemos almacenar sólo la secuencia de bits a la derecha del punto en la mantisa, por lo que los bits de la mantisa serán: b-1b-2b-3... Si esta secuencia de bits se almacena en los bits destinados a la mantisa se dice que el número está normalizado. En ambos casos como la mantisa sólo contiene p bits, la secuencia b-1b-2b-3... debe redondearse. Si el bit a la derecha del último bit que cabe en la mantisa es uno, se le sumará uno al último bit en la mantisa. En el formato para los números flotantes del IEEE, los tipos flotantes y dobles están normalizados. Rango de los Números Reales El rango de números reales que podemos almacenar en una localidad de memoria está determinado por el tamaño del exponente. El valor máximo en este rango Fmax, esta dado por: Fmax 1.111....1 2 eMax Fmax 2 2e Max Fmax 2e Max 1 El valor mínimo en este rango Fmin, esta dado por: Fmin 1.000....0 2 eMin 2 eMin La tabla 11-8 muestra el rango permitido para los valores de los números. ITSON Manuel Domitsu Kono 206 Bases Numéricas y Operadores Lógicos para Manejo de Bits Tabla 11-8. Rango de valores de los tipos flotantes. Tipos Rango float 2-126 - 2128 1.175 10-38 - 3.403 1038 double 2-1022 - 21024 2.225 10-308 - 1.193 10308 Precisión de los Números Reales La precisión es el número de cifras significativas en su mantisa o también la mínima diferencia que debemos tener entre dos números a fin de que la computadora los pueda manejar como distintos. Esta diferencia ocurre cuando ambos números difieren entre sí en la cifra menos significativa de la mantisa. Si un número binario tiene n cifras binarias significativas, la mínima diferencia que puede tener con otro número es 2-n. Para ese mismo número, pero en decimal su precisión será de m cifras decimales significativas y la diferencia mínima entre dos números será de 10-m. Como se trata de la misma precisión, las igualamos: 2-n = 10-m despejando m, tenemos que la precisión en cifras decimales significativas del número será: m = n log 2 Si el número no está normalizado, el número, n, de cifras binarias en la mantisa es de p. Si número está normalizado, el número, n, de cifras binarias en la mantisa es de p + 1. Recuerde que se sobrentiende el bit a la izquierda del punto, que siempre es uno. En la tabla 11-9 se muestran las precisiones de los tipos flotantes. Tabla 11-9. Precisión de los tipos flotantes. Precisión Precisión Tipos (Cifras binarias significativas) (Cifras decimales significativas) float 24 7 double 53 15 Por ejemplo represente el número flotante 758.25 tal como se almacenaría en una variable flotante. Convirtiendo el número 758.25 a binario en notación flotante, tenemos: 758.25 = 10,1111,0110.012 = 1.0111,1011,0012 29 Como el número es positivo, el bit de signo es 0. ITSON Manuel Domitsu Kono Capítulo 11 Bases Numéricas y Operadores Lógicos para Manejo de Bits 207 Como el número se va a almacenar como un flotante, el valor del exponente polarizado es de: eP = e + 127 = 9 + 127 = 136 que expresado en binario da: eP = 1000,10002 Como los números flotantes se representan normalizados, la mantisa se forma con sólo la parte fraccionaria del número binario en su representación flotante. Si es necesario se rellena con ceros a la derecha del bit menos significativo. m = 0111,1011,0010,0000,0000,000 Concatenando el bit de signo y los bits del exponente y la mantisa, tenemos: se m 758.25 = 0100,0100 0011,1101 1001,0000 0000,0000 = 0x443D9000 Como un segundo ejemplo, muestre como se almacenaría el número flotante -0.321 en una variable flotante. Convirtiendo el número -0.321 a binario en notación flotante, tenemos: -0.321 = -0.0101,0010,0010,1101,0000,1110,01... 2 = -1.0100,1000,1011,0100,0011,1001,... 22-2 El bit de signo es 1, ya que el número es negativo. El valor del exponente polarizado es de: epolarizado = e + 127 = -2 + 127 = 125 que expresado en binario da: epolarizado = 0111,1101 Note que se llena con ceros a la izquierda del bit más significativo a fin de llenar los 8 bits del exponente. La mantisa se forma con sólo la parte fraccionaria del número binario en su representación flotante. m = 0100,1000,1011,0100,0011,1001,... En la mantisa sólo caben 23 bits: ITSON Manuel Domitsu Kono 208 Bases Numéricas y Operadores Lógicos para Manejo de Bits m = 0100,1000,1011,0100,0011,100│1,... b-23 Como el bit b-24 es 1 le sumamos un 1 al bit b-23 m = 0100,1000,1011,0100,0011,101 Concatenando el bit de signo y los bits del exponente y la mantisa, tenemos: se m -0.321 = 1011,1110 1 010,0100 0101,1010 0001,1101 = 0xBEA45A1D Ejercicios 1. Muestra el patrón de bits, expresado como una secuencia hexadecimal, almacenados en las siguientes variables: a) float x =178.9521; b) float y = -0.183; 2. Encuentre el valor de las siguientes variables si su patrón de bits expresado en hexadecimal es el siguiente: a) float x; 0x43E5A140 b) float y; 0xBD6D0000 Entrada y Salida de Números Hexadecimales Podemos desplegar y leer números enteros sin signo en hexadecimal usando las funciones printf() y scanf(). En el caso de la función printf() el comando de formateo requerido tiene la siguiente sintaxis %[banderas][ancho][.precisión][{h|l}]{X|x} Si el especificador de tipo es X la función despliega usando los caracteres (A, B, C, D, E, F) para los números 10 a 15. Si el especificador de tipo es x, la función utiliza (a, b, c, d, e, f). Los modificadores de tipo h y l hacen que se interprete al entero como corto y largo respectivamente. Los especificadores de ancho se muestran en la tabla 11-10. ITSON Manuel Domitsu Kono Capítulo 11 Bases Numéricas y Operadores Lógicos para Manejo de Bits 209 Tabla 11-10. Especificadores de ancho Especificador Tamaño del ancho de campo de ancho n Se imprimen un mínimo de n caracteres. Si el valor a imprimirse tiene menos de n caracteres, se rellena con blancos. Se imprimen un mínimo de n caracteres. Si el valor a imprimirse tiene menos 0n de n caracteres, se rellena con ceros a la izquierda. El valor del especificador de ancho viene en la lista de argumentos, el cuál debe preceder al argumento a formatearse. * precisión.- Mínimo número de dígitos a imprimir. Si el número tiene menos caracteres que la precisión se rellena con ceros a la izquierda. Los tipos de especificadores de precisión se muestran en la tabla 11-11. Tabla 11-11. Especificadores de Precisión Especificador Descripción de precisión Ninguno Se usa la precisión por defecto: 1..0 1 si el valor de la expresión es diferente de cero; si es cero no despliega nada. n caracteres.n El valor del especificador de precisión viene en la lista de argumentos, el cuál debe preceder al argumento a formatearse..* banderas.- Especifica la justificación de la salida y prefijo hexadecimal. Las banderas pueden aparecer en cualquier orden y en cualquier combinación. Los tipos de banderas se muestran en la tabla 11-12. En el caso de la función scanf() el comando de formateo tiene la siguiente sintaxis %[ancho][{h|l}]{X|x}] Tabla 11-12. Banderas Bandera Significado Ninguna El resultado se justifica a la derecha rellenándose con blancos a la izquierda. - El resultado se justifica a la izquierda rellenándose con blancos a la derecha. # La cantidad aparece con el prefijo 0x ó 0X ITSON Manuel Domitsu Kono 210 Bases Numéricas y Operadores Lógicos para Manejo de Bits Se emplea el especificador de tipo x para leer enteros hexadecimales y X para leer enteros largos hexadecimales. Los modificadores de tipo h y l hacen que se interprete al entero hexadecimal leído como corto y largo respectivamente. Ejercicios Sean x = 1276, a = 10, b = 4. ¿Cuál es la salida que nos da la función printf() en los siguientes ejemplos?: a) printf("x = %x", x); b) printf("x = %06X", x); c) printf("x = %*.*x", a, b, x); d) printf("x = %-#8X", x); Operadores Lógicos de Bits Los operadores lógicos de bits sólo operan sobre operandos enteros (char, short, int, long, con o sin signo). Estos operadores permiten la manipulación de los bits individuales de sus operandos. Operador Complemento a Uno El operador unario complemento a uno, ~, complementa a uno todos los bits de su operando. Si un bit vale 0 lo convierte a 1 y si es 1 lo convierte a 0. Por ejemplo, sean las siguientes variables: unsigned short x = 283 short y = -175 x = 283 = 0000,0001,0001,1011 ~x = 65252 = 1111,1110,1110,0100 y = -175 = 1111,1111,0101,0001 ~y = 174 = 0000,0000,1010,1110 Operadores de Corrimiento El operador de corrimiento a la izquierda, > 2 = 5 = 0000,0000,0000,0101 ITSON Manuel Domitsu Kono 212 Bases Numéricas y Operadores Lógicos para Manejo de Bits y = 23 = 0000,0000,0001,0111 y >> 1 = 11 = 0000,0000,0000,1011 y >> 2 = 5 = 0000,0000,0000,0101 z = -23 = 1111,1111,1110,1001 z >> 1 = -12 = 1111,1111,1111,0100 z >> 2 = -6 = 1111,1111,1111,1010 Podemos ver que en el caso de que el operando izquierdo sea sin signo o con signo positivo, el corrimiento a la derecha de uno equivale a dividir el operando izquierdo por 2, un corrimiento a la derecha de 2 equivale a una división por cuatro y un corrimiento de n equivale a una división por x >> n = x / 2n Al igual que con los corrimientos a la izquierda, los corrimientos a la derecha son rápidos comparados con la división. Las divisiones enteras entre múltiplos de 2 pueden eficientarse usando corrimientos a la derecha. Operadores de Intersección, &, Unión Exclusiva, ^, y Unión Inclusiva, | El operador intersección para manejo de bits, &, nos da un entero en el que el i-ésimo bit es uno, solo si los dos i-ésimos bits de los operandos son unos, y es un cero en caso contrario. El operador unión exclusiva para manejo de bits, ^, nos da un entero en el que el i- ésimo bit es uno, solo si los dos i-ésimos bits de los operandos son diferentes, y es un cero en caso contrario. El operador unión para manejo de bits, |, nos da un entero en el que el i-ésimo bit es uno, si al menos uno de los dos i-ésimos bits de los operandos es uno y es un cero en caso contrario. Por ejemplo, sean las siguientes variables: unsigned short x = 283, y = 23 short z = 174, w = -23 x = 283 = 0000,0001,0001,1011 y = 23 = 0000,0000,0001,0111 x & y = 19 = 0000,0000,0001,0011 x ^ y = 268 = 0000,0001,0000,1100 x | y = 287 = 0000,0001,0001,1111 z = 174 = 0000,0000,1010,1110 w = -23 = 1111,1111,1110,1001 ITSON Manuel Domitsu Kono Capítulo 11 Bases Numéricas y Operadores Lógicos para Manejo de Bits 213 z & w = 257 = 0000,0000,1010,1000 z ^ w = -185 = 1111,1111,0100,0111 z | w = -17 = 1111,1111,1110,1111 Ejercicios 1. Suponga una variable de tipo unsigned char usada para almacenar el estado de un dispositivo y que cada uno de los bits de la variable representa una posible condición del dispositivo. Escriba una función que determine si el i_ésimo bit de la variable es cero o uno. La función debe devolver verdadero si el i_ésimo bit de la variable es uno, false en caso contrario. La sintaxis de la función es: int test (unsigned char x, int i); 2. Construya una función llamada codifica() que reciba tres parámetros enteros: a, b y c. La función combinará los bits de los parámetros para formar un byte de la siguiente manera: c0 b2 b1 b0 a3 a2 a1 a0 la función regresa el byte codificado. La sintaxis de la función es: unsigned char codifica(int a, int b, int c); Problemas 1. Convierte los siguientes números decimales a binarios y hexadecimales: a) 243.1024 b) 1.31710-2 2. Convierte los siguientes números binarios a decimales y hexadecimales: a) 10,0101.10101 b) 1.011,00002-4 3. Convierte los siguientes números hexadecimales a binarios y decimales: a) A7C.D4E b) A.31716-2 4. Encuentre los complementos de 9 y de 10 de los siguientes números decimales a) 37810 ITSON Manuel Domitsu Kono 214 Bases Numéricas y Operadores Lógicos para Manejo de Bits b) 390.3732 5. Encuentre los complementos de 1 y de 2 de los siguientes números binarios a) 100010112 b) 1110.1001112 6. Efectúe las siguientes substracciones usando complemento de 10 a) 8156 - 6652 b) 435.69 - 1823.138 7. Efectúe las siguientes substracciones usando complemento de 2 a) 10010110 - 101101 b) 11100.0110 - 101100.11 8. Muestra el patrón de bits, como una secuencia hexadecimal, almacenados en las siguientes variables (Considere que los tipos unsigned e int son de 32 bits): a) unsigned char x = 135; b) char y = -57; c) unsigned z = 2348; d) int w = -1549; 9. Encuentre el valor de las siguientes variables si su patrón de bits, como una secuencia hexadecimal, es el siguiente (Considere que los tipos unsigned e int son de 32 bits): a) unsigned char x: 0xE1 b) char y: 0xE1 c) unsigned z: 0xA15C d) int w: 0xA15C 10. Muestra el patrón de ceros y unos almacenados en las siguientes variables: a) float x = 1578.9321; b) float y = -0.0731; 11. Encuentre el valor de las siguientes variables si su patrón de bits, expresado en hexadecimal, es el siguiente: a) float x: 0x4361A150 b) float y: 0xBD61E000 12. Suponga que C definiera el tipo de datos big int como un entero de tres bytes. Determine el rango de enteros permisibles en: a) Una variable unsigned big int b) Una variable signed big int ITSON Manuel Domitsu Kono Capítulo 11 Bases Numéricas y Operadores Lógicos para Manejo de Bits 215 13. Suponga que C definiera el tipo de datos big float como un flotante de tres bytes. Si el signo ocupa un bit y el exponente 11, determine el rango y la precisión de un número flotante si se encuentra en forma normalizada. 14. Suponga que C definiera el tipo de datos extended float como un flotante de tres bytes. Si el signo ocupa un bit y el exponente 8, determine el rango y la precisión de un número flotante si se encuentra en forma no normalizada. 15. Suponga las siguientes declaraciones: unsigned char x = 123, y = 73, z = 59; Determine el valor de las siguientes expresiones: a) (x >> 3)