Apéndice A: Codificación ASCII
El origen
Estamos acostumbrados a que los ordenadores manejen cualquier tipo de información: números, textos, gráficos, sonidos, fórmulas... Sin embargo, en realidad, los ordenadores sólo pueden manejar números, y más concretamente, sólo ceros y unos.
De hecho, si profundizamos más, incluso esto es una convención, y en lo más profundo, lo único que encontraremos en un ordenador son células básicas que pueden contener y manipular dos estados. A uno de esos estados se le asigna valor lógico cero, y al otro un uno.
Los técnicos electrónicos llaman a cada uno de estas células biestable, precisamente, porque pueden tener dos estados. A cada uno de esos estados se le llama bit. Un bit es la unidad mínima de información.
Desde los primeros ordenadores, acceder a bits como unidad ya resultaba poco práctico, así que los bits se agruparon formando unidades mayores.
En la práctica, nosotros hacemos lo mismo con los números, para manejar números mayores de nueve usamos dos dígitos, de modo que cada uno de ellos tiene un valor diferente según la posición que ocupe. Por ejemplo, en el número 23, el 2 tiene un peso 10 veces mayor que el 3, ya que ocupa una posición más a la izquierda. Si usamos tres dígitos, el tercero por la derecha tiene un peso 100 veces mayor que el primero, y así suscesivamente.
Los primeros ordenadores usaban unidades de cuatro bits, nibbles. El motivo era, sencillamente, simplificar el acceso a la información por parte de los humanos. Para almacenar un dígito en base 10, que son los que usamos nosotros, se necesitan al menos cuatro dígitos binarios.
Veamos esto. Análogamente a lo que pasa con nuestros números en base 10, cada posición contando desde la derecha será dos veces mayor que la anterior. El primer dígito, desde la derecha, tiene peso 1, el segundo 2, el tercero 4, el cuarto 8, etc.
Estas relaciones de peso son potencias de la base de numeración elegida. En base 10 son potencias de 10: 100 (=1), 101 (=10), 102 (=100), 103 (=1000), 104 (=10000), etc. En base 2 son potencias de dos: 20 (=1), 21 (=2), 22 (=4), 23 (=8), 24 (=16), etc.
Así, con tres bits tenemos que el número mayor que podemos codificar es "111", es decir:
1*22+1*21+1*20 = = 1*4+1*2+1*1 = 4+2+1 = 7
Esto es insuficiente para codificar números entre 0 y 9, nos faltan dos valores, por lo tanto, necesitamos otro bit. Con cuatro podemos llegar hasta:
1*23+1*22+1*21+1*20 = = 1*8+1*4+1*2+1*1 = 8+4+2+1 = 15
Con esto, en realidad, nos sobran seis valores, pero podemos ignorarlos (de momento).
Esta forma de codificación se denomina BCD, es decir, Decimal Codificado en Binario. La tabla de valores BCD es la siguiente:
Código binario | Dígito decimal |
---|---|
0000 | 0 |
0001 | 1 |
0010 | 2 |
0011 | 3 |
0100 | 4 |
0101 | 5 |
0110 | 6 |
0111 | 7 |
1000 | 8 |
1001 | 9 |
Durante un tiempo, los ordenadores trabajaban con palabras de cuatro bits, siempre se ha llamado palabras a las agrupaciones de bits, y según la tecnología ha ido avanzando, las palabras han ido teniendo más bits.
Para simplificar y sobre todo, para conseguir mejores resultados, el acceso a la memoria también se hace mediante palabras. El procesador no accede a bits de memoria, sino a palabras, y la memoria se organiza por palabras. A cada una de esas palabras se le asigna una dirección: una dirección de memoria.
De modo que en un procesador de cuatro bits, cada dirección de memoria contiene un nibble, y en cada nibble se pueden almacenar un dígito decimal en la forma de dígitos BCD o una instrucción del proceador.
Entre los avances más destacados de los procesadores están el de usar la misma memoria para almacenar los datos y los programas. El procesador puede leer una instrucción de programa desde la memoria, ejecutarla, pasar a la siguiente instrucción, y repetir el proceso.
Siguiendo la misma idea, se puede acceder a varias direcciones contiguas de memoria para almacenar números mayores o instrucciones más complejas (16 instrucciones son pocas incluso para un ordenador primitivo), con dos nibbles tenemos posibilidad de almancenar 256 instrucciones.
Las palabras de cuatro bits se quedaron pequeñas pronto, y rápidamente se pasó a palabras y a procesadores de 8 bits. La unidad de 8 bits se conoce como octeto o byte, y este tamaño tuvo tanto éxito que sigue siendo hoy en día la unidad básica para acceder a memoria.
Procesadores posteriores usan palabras que agrupaban bytes, los de 16 bits usan dos bytes, los de 32 usan cuatro bytes, y los de 64 ocho bytes. Sin embargo, se sigue usando una dirección de memoria para cada byte.
Los primeros procesadores eran en realidad máquinas calculadoras programables, es decir, sólo manejaban números y se usaban para realizar cálculos numéricos complejos o repetitivos. Pero según fue aumentando la complejidad de las máquinas, surgieron nuevas posibilidades, como proporcionar salidas más cómodas para las personas.
Pero los ordenadores siguen manipulado sólo números, por lo que había que inventar algo para que pudieran manejar letras y palabras. La solución es simple, y consiste en codificar cada letra mediante un valor numérico. De este modo nació el código ASCII.
El primer código ASCII almacenaba cada letra en un byte, pero usaba sólo 7 bits, dejando el octavo para añadir un bit de control de errores, que permite detectar fallos en las transmisiones de datos. Este bit se llama bit de paridad, y funciona del modo siguiente:
Para cada código ASCII se suman los bits con valor 1, si se usa un control de paridad par la suma de los bits en cada byte debe ser par (contando el bit de paridad), si se usa un control de paridad impar, la suma debe ser impar.
De todos modos, con siete bits se pueden codificar 128 caracteres, que según las personas que diseñaron el código, eran más que suficientes. Esto permite codificar los 10 digitos numéricos, los 25 caracteres del alfabeto inglés en mayúsculas, otros 25 para las minúsculas, 32 para caracteres destinados a símbolos, paréntesis, y signos de puntuación, el espacio. El resto, hasta 127 (33 caracteres), se usaron para codificar caracteres no imprimibles, que permiten formatear texto: retornos de línea, avances y retrocesos de caracteres, caracteres destinados a protrocolos de transmisión, etc.
Tabla ASCII
Tabla ASCII correspondiente a los primeros 32 caracteres y al último. Estos son los no imprimibles:
Código binario | Código decimal | Abreviatura | Nombre |
---|---|---|---|
00000000 | 0 | NUL | Caracter Nulo (NULL) |
00000001 | 1 | SOH | Inicio de Encabezado (Start Of Header) |
00000010 | 2 | STX | Inicio de Texto (Start Text) |
00000011 | 3 | ETX | Fin de Texto (End Text) |
00000100 | 4 | EOT | Fin de Transmisión (End Of Transmision) |
00000101 | 5 | ENQ | Pregunta (Enquiry) |
00000110 | 6 | ACK | Reconocimiento (Acknowledgement) |
00000111 | 7 | BEL | Timbre (Bell) |
00001000 | 8 | BS | Retroceso (Back Space) |
00001001 | 9 | HT | Tabulación horizontal (Horizontal Tab) |
00001010 | 10 | LF | Avance de Línea (Line feed) |
00001011 | 11 | VT | Tabulación Vertical (Vertical Tab) |
00001100 | 12 | FF | Salto de página (Form feed) |
00001101 | 13 | CR | Retorno de carro (Carriage return) |
00001110 | 14 | SO | Salida de turno (Shift Out) |
00001111 | 15 | SI | Entrada de turno (Shift In) |
00010000 | 16 | DLE | Escape de enlace de datos (Data Link Escape) |
00010001 | 17 | DC1 | Device Control 1 — oft. XON |
00010010 | 18 | DC2 | Device Control 2 |
00010011 | 19 | DC3 | Device Control 3 — oft. XOFF |
00010100 | 20 | DC4 | Device Control 4 |
00010101 | 21 | NAK | Reconocimiento negativo (Negative Acknowledgement) |
00010110 | 22 | SYN | Sincronismo sin usar (Synchronous Idle) |
00010111 | 23 | ETB | Fin de transmisión de bloque (End of Trans. Block) |
00011000 | 24 | CAN | Cancelar (Cancel) |
00011001 | 25 | EM | Fin de soporte (End of Medium) |
00011010 | 26 | SUB | Sustituto (Substitute) |
00011011 | 27 | ESC | Escape |
00011100 | 28 | FS | Separador de fichero (File Separator) |
00011101 | 29 | GS | Separador de grupo (Group Separator) |
00011110 | 30 | RS | Separador de registro (Record Separator) |
00011111 | 31 | US | Separador de unidad (Unit Separator) |
01111111 | 127 | DEL | Borrar (Delete) |
Tabla ASCII correspondiente a los caracteres imprimibles:
Binario | Decimal | Carácter | Binario | Decimal | Carácter | Binario | Decimal | Carácter |
---|---|---|---|---|---|---|---|---|
00100000 | 32 | espacio | 01000000 | 64 | @ | 01100000 | 96 | ` |
00100001 | 33 | ! | 01000001 | 65 | A | 01100001 | 97 | a |
00100010 | 34 | " | 01000010 | 66 | B | 01100010 | 98 | b |
00100011 | 35 | # | 01000011 | 67 | C | 01100011 | 99 | c |
00100100 | 36 | $ | 01000100 | 68 | D | 01100100 | 100 | d |
00100101 | 37 | % | 01000101 | 69 | E | 01100101 | 101 | e |
00100110 | 38 | & | 01000110 | 70 | F | 01100110 | 102 | f |
00100111 | 39 | ' | 01000111 | 71 | G | 01100111 | 103 | g |
00101000 | 40 | ( | 01001000 | 72 | H | 01101000 | 104 | h |
00101001 | 41 | ) | 01001001 | 73 | I | 01101001 | 105 | i |
00101010 | 42 | * | 01001010 | 74 | J | 01101010 | 106 | j |
00101011 | 43 | + | 01001011 | 75 | K | 01101011 | 107 | k |
00101100 | 44 | , | 01001100 | 76 | L | 01101100 | 108 | l |
00101101 | 45 | - | 01001101 | 77 | M | 01101101 | 109 | m |
00101110 | 46 | . | 01001110 | 78 | N | 01101110 | 110 | n |
00101111 | 47 | / | 01001111 | 79 | O | 01101111 | 111 | o |
00110000 | 48 | 0 | 01010000 | 80 | P | 01110000 | 112 | p |
00110001 | 49 | 1 | 01010001 | 81 | Q | 01110001 | 113 | q |
00110010 | 50 | 2 | 01010010 | 82 | R | 01110010 | 114 | r |
00110011 | 51 | 3 | 01010011 | 83 | S | 01110011 | 115 | s |
00110100 | 52 | 4 | 01010100 | 84 | T | 01110100 | 116 | t |
00110101 | 53 | 5 | 01010101 | 85 | U | 01110101 | 117 | u |
00110110 | 54 | 6 | 01010110 | 86 | V | 01110110 | 118 | v |
00110111 | 55 | 7 | 01010111 | 87 | W | 01110111 | 119 | w |
00111000 | 56 | 8 | 01011000 | 88 | X | 01111000 | 120 | x |
00111001 | 57 | 9 | 01011001 | 89 | Y | 01111001 | 121 | y |
00111010 | 58 | : | 01011010 | 90 | Z | 01111010 | 122 | z |
00111011 | 59 | ; | 01011011 | 91 | [ | 01111011 | 123 | { |
00111100 | 60 | < | 01011100 | 92 | \ | 01111100 | 124 | | |
00111101 | 61 | = | 01011101 | 93 | ] | 01111101 | 125 | } |
00111110 | 62 | > | 01011110 | 94 | ^ | 01111110 | 126 | ~ |
00111111 | 63 | ? | 01011111 | 95 | _ |
Las letras son números
Bueno, creo que ahora queda más claro por qué se puede usar un valor entero como un carácter o como un número. Dentro de un ordenador no existe diferencia entre ambas cosas.
El valor 65 puede ser un número entero, o, si se interpreta como un carácter, puede ser la letra 'A'.
Manejar signos
Pero aún nos queda un detalle importante: el signo. Cuando hemos hablado de variables de tipo char hemos comentado que pueden ser con y sin signo. Veamos cómo se las arregla el ordenador con los signos.
Lo primero que podemos decir del signo es que hay dos posibilidades: puede ser positivo o negativo. Esto parece hecho a la medida de un bit, de modo que podemos usar un único bit para indicar el signo, de modo que un valor 0 indica que se trata de un número positivo y un 1 de un número negativo.
Esta es la primera solución, podemos usar el bit más a la izquierda para codificar el signo. Con un número de ocho bits, eso nos deja siete para codificar el valor absoluto, es decir, 127 valores posibles positivos y otros tantos negativos.
Pero esta solución tiene dos inconvenientes:
- Tenemos dos codificaciones diferentes para el cero. No es un inconveniente pequeño, ya que dificulta las comparaciones y crea muchos casos particulares.
- La aritmética se complica en cuanto a sumas y restas. Veremos que esto es sólo en comparación con otros sistemas de codificación.
Existe una solución mejor que mantiene la codificación del bit de signo, pero que elimina estos dos inconvenientes. Se trata de la codificación conocida como "complemento a dos".
Pero antes de ver qué es el complemento a dos, veamos qué es el complemento a uno. El complemento a uno consiste, en tomar un número en binario y cambiar los ceros por unos y los unos por ceros.
El complemento a uno de 01001010 es 10110101.
El complemento a dos consiste en hacer el complemento a uno y sumar 1 al resultado.
Ya sabemos sumar, espero, con números en base diez. Con números en base dos es igual de fácil. La tabla de sumar en binario es:
- 0 + 0 = 0
- 0 + 1 = 1
- 1 + 0 = 1
- 1 + 1 = 10, es decir: 0 y nos llevamos 1
- 1 + 1 + 1 = 11, es decir 1 y nos llevamos 1
Por ejemplo:
111111
01101011
+ 00110110
----------
10100001
Sigamos con el complemento a dos. Para el ejemplo anterior, el complemento a dos de 01001010 sería 10110101+1:
1
10110101
+ 00000001
----------
10110110
Lo primero que vemos es que cuando se hace el complemento a dos de cualquier número, el signo cambia. Otra cosa que podemos ver es que el complemento a dos del cero es cero.
Complemento a uno de 00000000 es 11111111, y sumando 1 tenemos:
11111111 11111111 + 00000001 ---------- 100000000
El uno de la izquierda no cuenta, ya que sólo tenemos ocho bits, el noveno se pierde, y el resultado es cero.
Pero lo mejor de todo es que la suma de cualquier número con su complemento a dos es siempre cero:
1111111 01001010 + 10110110 ---------- 100000000
Esto es una gran ventaja, ya que podemos usar esta propiedad para decir que el complemento a dos equivale a cambiar el signo de un número, ya que la suma de un número y su complemento es cero, y el complemento de cero es cero.
Esto además nos da otra pequeña ventaja. Al tener una sóla combinación para el cero, tenemos un valor extra, de modo que el valor positivo máximo es 127 (01111111), pero para los negativos podemos llegar hasta el -128 (10000000).
Así, si usamos una variable char sin signo para almacenar números, podremos manejar valores entre 0 y 255. Si usamos variables char con signo, los valores posibles estarán entre -128 y 127.