Números dentro del hardware de la computadora
Comprender conceptos matemáticos como el cambio de base de los números, las funciones exponenciales y logarítmicas, entre otros, te permitirá tener una comprensión más profunda de algoritmos y estructuras de datos utilizados en programación. Además, estos conceptos pueden ser útiles en la resolución de problemas matemáticos relacionados con la programación, como la optimización de algoritmos y la evaluación de la complejidad computacional.
Asimismo, el conocimiento de conceptos matemáticos puede
ayudarte a entender y diseñar algoritmos para solucionar problemas más
complejos, por ejemplo, en áreas como la inteligencia artificial, el
aprendizaje automático y la criptografía.
Por lo tanto, aunque no sea necesario comprender estos
conceptos para ciertas actividades específicas en programación, tener un
conocimiento sólido de matemáticas y sus aplicaciones en programación puede
ayudarte a ser un mejor desarrollador y a tener un conjunto más amplio de
habilidades que te permitan resolver problemas de manera más efectiva y
eficiente.
Nociones previas
Cómo ya es sabido, podemos agrupar los números en conjuntos
de modo que unos sean subconjuntos de otros. Veamos un ejemplo:
El conjunto de números naturales es:
N = {1, 2, 3, 4, 5, …}
El conjunto de los números enteros es:
Z = {…, --5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, …}
Si observamos detenidamente en el conjunto de los números
enteros “Z”, a partir del 0 a la derecha, encontramos todos los números
naturales. Por lo tanto, los números naturales son un subconjunto de los
números enteros, o en simbología matemática “N” ⊂ “Z”.
Ahora veamos todos lo conjuntos de
números
Naturales: N = {1, 2, 3, 4, 5, …}
Enteros: Z = {…, --5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, …}
Racionales: Q = {…, -5, -4.999, -4.99801, -4.998009, …, 0,
0.001, …} (son números decimales que
pueden ser expresados en una fracción, es decir por más larga que sea la parte
decimal siempre habrá un tope. Las únicas excepciones a esta regla, son los
decimales periódicos puros y mixtos, que también son considerados racionales,
ya que pueden ser expresados en fracción)
Irracionales: I = { e,π,√2, …} (son números decimales que no pueden ser expresados en fracción, debido a que su parte decimal es infinita y no periódica)
Reales: R = { Todos los anteriores
juntos}
Notación científica
Para representar números en notación
científica, es necesario ajustar el número para que su parte entera tenga una
sola cifra diferente de 0. Para lograr esto, se debe multiplicar y dividir por
potencias de diez de manera conveniente, sin modificar el valor del número
original.
A continuación, se muestra un ejemplo práctico: para expresar el número 14789 en notación científica podemos escribirlo como 14789.0, de modo que ubiquemos la coma, y luego dividirlo entre 10000, para obtener una parte entera igual a 1. Finalmente, se debe multiplicar el resultado por 10000 para mantener el valor original del número.
Veamos otro ejemplo:
Solución:
Este número ya tiene una potencia de 10. Sin embargo, aún no tiene la forma adecuada. Dividimos entre 1000 y multiplicamos
por 1000 para no alterar el valor.
Notación científica en el sistema binario
Un número se puede representar en
notación científica para bases diferentes de 2 y 10. No obstante, para este
punto nos enfocaremos en cómo se expresa un número binario en notación científica.
Tomando como ejemplo el número 14789, lo primero que debemos hacer es
expresarlo en sistema binario.
14 789 = (11100111000101)2
De manera similar a los ejemplos anteriores, debemos dividir entre potencias de dos y multiplicar por potencias de dos, de tal manera que el número original no se altere.
20189.375 = (100111011011101.011)2
Cambio de base:
Los números se expresan en un sistema determinado
mediante su subíndice final, y los números sin subíndice se asumen en el
sistema decimal por defecto. Es posible representar un número en una base
diferente y conservar su valor original. Aunque este concepto puede resultar
familiar, es importante tenerlo en cuenta para evitar caer en la trampa de
pensar que algunos números no lo cumplen. Por ejemplo, la fracción 1/10 se
encuentra en el sistema decimal y, al ser comúnmente utilizado, no se indica su
base. Sin embargo, al convertirla al sistema binario, se obtiene (0.0 0011 0011
0011 0011 …)2, donde se ha separado los decimales para destacar la
periodicidad en el decimal infinito.
A simple vista, podría parecer que el
número en el sistema binario no debería ser igual a su equivalente en el
sistema decimal debido a su infinita cantidad de decimales. Sin embargo, como
se mencionó anteriormente, eso no es cierto.
Ejemplos de números expresados en una cierta base:
(10001)2
(456712)10 = 456712
(12102)3
(713457)8
Para cambiar un número de base 10 a otra
base diferente, se debe dividir el número original entre el valor de la base a
la que se dese cambiar, luego el cociente y así sucesivamente.
Ejemplos:
(8945)10 a base 2
Luego de realizar las
divisiones sucesivas hasta ya no poder continuar, el número 8945 estará representado
en base 2 colocando los resultados obtenidos, del último (en rojo) al primero.
(8945)10 = (10001011110001)2
(8945.375)10 a base 2
La parte entera se puede calcular por el método anterior:
(8945)10 = (10001011110001)2
Para convertir la parte fraccionaria (0.375) en binario, se debe seguir un proceso específico. Primero, se multiplica sucesivamente por 2 y se observa si el resultado es menor que 1. Si es así, se representa esta operación con 0. Si es mayor o igual a 1, se representa con un 1. Cuando el resultado sea exactamente 1, se detienen las multiplicaciones y se obtiene el resultado final anotando los 0s y 1s en el orden que se obtuvieron.
Como calculamos un número fraccionario,
se agrega “0.” antes de los bits obtenidos de la
siguiente forma: (0.011)2
Si deseamos convertir un número que se
encuentra en una base distinta a la base diez a base diez, podemos utilizar la descomposición
polinómica.
Ejemplo:
(10110111)2 a base 10
Solución:
Se separan los dígitos y cada uno se
multiplica por una potencia de dos que aumenta consecutivamente de derecha a izquierda.
Entonces
(10110111)2 = (183)10
Para números con parte fraccionaria, se
realiza el mismo proceso, pero disminuyendo el valor del exponente hacia la
derecha. En otras palabras, se multiplica por la inversa de las potencias de 2.
Ejemplo:
(1011.1101)2 a base 10
Solución:
La parte entera (antes del punto) se encuentra en azul y la parte fraccionaria
(después del punto) se encuentra en rojo.
Entonces
(1011.1101)2 = (11.8125)10
Representación de los números dentro del
hardware de la computadora
Probablemente has leído o escuchado que en
lenguaje de máquina se utilizan unos y ceros, también conocidos como bits. Esto
es cierto, y es necesario convertir los números del sistema decimal al sistema
binario para poder almacenarlos y trabajar con ellos en la computadora.
Un bit es la abreviatura de “dígito
binario” y representa un elemento en memoria que puede estar encendido o apagado.
Al igual que un interruptor, un bit puede ser representado como 1 si está
encendido o como 0 si está apagado. Un conjunto de 8 bits se llama byte.
(Nakamura, 1992)
Actualmente, la mayoría de las
computadoras usan arquitecturas de 64 bits, pero algunos dispositivos pueden
trabajar con arquitecturas de 32 bits, como es el caso de Raspberry Pi.
Para almacenar números enteros en una
computadora con arquitectura de 32 bits, se utiliza el primer bit para
representar el signo del número: positivo si es 0, negativo si es 1. Los 31
bits restantes se utilizan para almacenar el número en sistema binario.
Es importante mencionar que el autor
Nakamura, en su libro “Métodos numéricos aplicados con Software”, sigue los
mismos pasos para distintas arquitecturas, incluyendo la de 16 bits. Sin embargo,
esto se aplica al ejemplo tomado de 32 bits e igualmente para 64 bits.
Ejemplo:
Se desea almacenar el número 7843, por lo que lo convertimos a binario y, dado que es positivo, nuestro primer bit a la izquierda lo lema
(7843)10 = (1111010100011)2
Dado a que el número en cuestión es positivo,
el primer bit lo en la tabla inferior se rellena con 0. Luego, se coloca el número
binario en la mantisa y, debido a que tiene 13 dígitos, los 18 espacios sobrantes
los rellenamos con 0.
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
1 |
1 |
1 |
0 |
1 |
0 |
1 |
0 |
0 |
0 |
1 |
1 |
Límites de la precisión simple
Tomando como punto de partida el ejemplo
anterior, surge la pregunta de qué sucedería si en lugar de rellenar los espacios
de la mantisa con ceros y unos, se utilizara solo el valor de 1 para
completarlos. Es decir, ¿qué ocurriría si se representara el número utilizando
la máxima cantidad de bits de en la mantisa y estos tuvieran un valor de 1?
Esta cuestión tiene una respuesta sencilla, pero tal vez poco intuitiva.
Veamos el planteamiento en una tabla
llenando con unos toda la mantisa y asignando 0 al bit del signo.
0 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
Cambiando de sistema la mantisa: (1111111111111111111111111111111)2
= (2147483647)10
Esto nos da el mayor número que se
puede representar con 32 bits: 231-1 = (2147483647)10
Ahora bien, en el caso de asignar el
valor 1 al bit del signo y aplicar el razonamiento anterior para la mantisa, se
obtiene el menor número que se puede representar en 32 bits.
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
Cambiando de sistema la mantisa: (1111111111111111111111111111111)2
= (2147483647)10
Al agregar el signo, el menor número
que se puede representar es -(231-1) = -(2147483647)10
¿Cómo es posible que el mayor dígito sea
(231-1)?
Aunque pueda parecer poco intuitivo, te
invito a seguir el razonamiento que expondré a continuación. En este sistema de
representación numérica que estamos considerando, se utilizan un total de 32
bits, de los cuales 31 se utilizan para expresar el número en sí y uno para el
exponente. Los 31 bits pueden ser llenados con diferentes combinaciones de unos
y ceros, dependiendo del número que se quiera almacenar en sistema binario. Como
es de esperar, el mayor número que puede ser almacenado es aquel en el que
todos los bits son iguales a 1.
Cabe destacar que, en el sistema binario,
cuando se suma un 1 al número completo, se lleva 1 para la siguiente posición
de los dígitos. Por ejemplo:
(1000)2 + (1)2 = (1001)2
(1001)2 + (1)2 = (1010)2
(1010)2 + (1)2 = (1011)2
(1011)2 + (1)2 = (1100)2
(1100)2 + (1)2 = (1101)2
(1101)2 + (1)2 = (1110)2
(1110)2 + (1)2 = (1111)2
(1111)2 + (1)2 = (10000)2
(10000)2 + (1)2 = (10001)2
Según este patrón, si a nuestro número
máximo (1111111111111111111111111111111)2 le sumamos (1)2,
se obtendría (10000000000000000000000000000000)2, un 1 acompañado
de 31 veces 0. Si usamos la descomposición polinómica para convertirlo a
sistema decimal se obtiene 231, lo que es igual a nuestro número
máximo más (1)2. Por lo que, si queremos volver al número máximo,
debemos operar 231-(1)2, y dado que (1)2 = (1)10
(Anexo A), el número máximo que podemos guardar en 32 bits es 231-1.
Si todavía no está claro este punto, se
recomienda repasarlo varias veces, ya que lo que se explicará a continuación
será más fácil de seguir, pero requerirá de estas herramientas previas y, a pesar de que
este planteamiento es correcto, las máquinas utilizan una representación más didáctica
llamada “complemento a dos” para almacenar números negativos. (Nakamura, 1992)
Representación en punto flotante
Los números reales se representan por
medio de los formatos de punto flotante. Hay cinco formatos básicos definidos:
tres formatos binarios, con codificación en longitudes de 32, 64 y 128 bits y
dos formatos decimales, con codificación en longitudes de 64 y 128 bits.
Los formatos binarios, son los más usados
por hardware y lenguajes de programación. Estos brindan una representación en
punto flotante para los números reales bajo la definición del estándar IEEE 754
(Institute of Electrical and Electronic Engineers).
Hay dos tipos de precisión según el
número de bits utilizados para almacenar al número en punto flotante: precisión
simple para 32 bits y precisión doble para 64 bits.
Para la representación en punto flotante,
se debe expresar el número en el sistema binario, y luego en notación
científica normalizada y finalmente ubicar el número en los bits a disposición.
Veamos el ejemplo de almacenar el número 1002144.12 en precisión simple.
Paso 1: Convertir a binario
1002144.12 = (11110100101010100000.011)2
Paso2: Convertir a notación científica
normalizada
La notación científica normalizada, en
síntesis, es la notación científica que se ha visto en la sección de nociones
básicas al inicio del texto, con la pequeña diferencia de que la parte entera
debe ser 0, obligatoriamente.
Paso 3: Almacenar el número obtenido bajo
el estándar IEEE 754 para precisión simple.
De acuerdo al estándar, los 32 bits (4
bytes) se separan en tres partes: 1 bit para el signo, 8 bits para el exponente
y 23 bits para la mantisa.
Comentarios