Saltar al contenido
Topos Uranos

Resumen

Un entero del programa no es un entero de la matemática: es una sucesión finita de bits, y con nn bits caben exactamente 2n2^n valores distintos. Esta clase estudia esa representación y sus consecuencias. Enseña a escribir un entero en base 2 y en base 16 y a comprobarlo con los campos de formato {:b} y {:x}; presenta los tipos enteros de C++, su tamaño, que se mide con sizeof, y su rango, que se consulta con std::numeric_limits; explica cómo se representan los negativos mediante el complemento a dos y por qué el rango no es simétrico; distingue los enteros con signo de los enteros sin signo, y estudia lo que ocurre cuando una operación sale del rango: en los sin signo, la norma define el resultado, que es aritmética módulo 2n2^n; en los con signo, no lo define, y el programa queda fuera de toda garantía. Termina con un criterio para elegir el tipo de cada dato.

Objetivos de aprendizaje

  1. Convertir enteros entre las bases 10, 2 y 16, y comprobar la conversión con los campos de formato {:b} y {:x}.
  2. Calcular el rango de un entero de nn bits con signo y sin signo, y comprobarlo con std::numeric_limits y sizeof.
  3. Explicar el complemento a dos y escribir la representación en ocho bits de un negativo pequeño.
  4. Distinguir el desbordamiento con signo, que es comportamiento indefinido, del desbordamiento sin signo, que es aritmética módulo 2n2^n, y predecir el resultado del segundo.
  5. Elegir el tipo entero adecuado para un dato según su rango previsible, usando los tipos de anchura fija de <cstdint> cuando la anchura importa.

Sistemas posicionales

La clase Variables y memoria presentó la variable como una casilla con nombre, tipo y valor. Ahora bien, la memoria no guarda cifras decimales: guarda bits, cada uno de los cuales vale 0 o 1, agrupados en bytes de ocho bits. Para saber qué valores puede tomar una variable entera hay que saber, por tanto, cómo se escribe un número con solo dos cifras, y eso es lo que hace un sistema posicional de base 2.

En un sistema posicional de base bb, cada cifra vale según la posición que ocupa: la última multiplica a b0=1b^0 = 1, la anterior a b1b^1, la anterior a b2b^2, y así sucesivamente. En base 10, 20262026 significa 2⋅103+0⋅102+2⋅101+6⋅1002 \cdot 10^3 + 0 \cdot 10^2 + 2 \cdot 10^1 + 6 \cdot 10^0. En base 2 las cifras son 0 y 1, y los pesos son las potencias de 2: 1,2,4,8,16,32,64,128,…1, 2, 4, 8, 16, 32, 64, 128, \ldots De ello se sigue la regla para pasar de binario a decimal: se suman los pesos de las posiciones donde hay un 1.

EjemploDe binario a decimal

La figura lee el patrón de ocho bits 00101101. Sobre cada casilla está su peso; los unos se encienden uno a uno, de izquierda a derecha, y el valor acumula sus pesos.

Demostración

  1. Ocho casillas, todas apagadas: el valor es 0. Los pesos, de derecha a izquierda, son 1,2,4,…,1281, 2, 4, \ldots, 128.

  2. El primer uno está en la posición de peso 32: el valor acumulado es 32.

  3. El siguiente, en la de peso 8: 32+8=4032 + 8 = 40.

  4. El siguiente, en la de peso 4: 40+4=4440 + 4 = 44.

  5. El último, en la de peso 1: 44+1=4544 + 1 = 45. El patrón 00101101 representa el número 45.

  6. Agrupados de cuatro en cuatro, los bits dan las cifras hexadecimales: 0010 es 2 y 1101 es 13, que en base 16 se escribe d. Por tanto, 45 se escribe 2d en base 16.

El camino inverso, de decimal a binario, se recorre por divisiones sucesivas: se divide el número por 2, se anota el resto (0 o 1) y se repite con el cociente hasta llegar a 0; los restos, leídos desde el último hasta el primero, son las cifras binarias. Para 45: 45=2⋅22+145 = 2 \cdot 22 + 1, 22=2⋅11+022 = 2 \cdot 11 + 0, 11=2⋅5+111 = 2 \cdot 5 + 1, 5=2⋅2+15 = 2 \cdot 2 + 1, 2=2⋅1+02 = 2 \cdot 1 + 0, 1=2⋅0+11 = 2 \cdot 0 + 1; leídos al revés, los restos dan 101101, que con dos ceros a la izquierda es el patrón de la figura. La razón es que el último resto es la cifra de las unidades, y cada cociente es el número desplazado una posición a la derecha. El mismo método vale para cualquier base: para base 16 se divide por 16, y los restos de 10 a 15 se escriben con las letras a a f.

La base 16, o hexadecimal, existe por comodidad: como 16=2416 = 2^4, cada cifra hexadecimal corresponde exactamente a cuatro bits, y un byte se escribe con dos cifras. Un número de 32 bits ocupa treinta y dos cifras en binario, que se leen mal, y solo ocho en hexadecimal, que se convierten a binario sin cálculo, cifra por cifra. Por eso los programadores leen la memoria en hexadecimal. El fundamento matemático de los sistemas posicionales se estudia en el curso Álgebra y Geometría I, en la clase «Sistemas de numeración posicional».

C++ admite enteros escritos directamente en estas bases: un literal que empieza con 0b está en binario, y uno que empieza con 0x, en hexadecimal. Dentro de cualquier literal puede ponerse un apóstrofo como separador de cifras, que el compilador ignora y el lector agradece: 0b0010'1101, 1'000'000. En la salida, el campo de formato {:b} escribe un entero en binario, {:x} en hexadecimal con minúsculas y {:X} con mayúsculas; el signo # antepone el prefijo, y un ancho precedido de un cero rellena con ceros a la izquierda.

EjemploUn mismo número, tres escrituras
Programa en C++
#include <print>​int main(){    const int decimal{45};    const int binary{0b0010'1101};    const int hexadecimal{0x2d};    std::println("{} {} {}", decimal, binary, hexadecimal);    std::println("binario: {:b}", decimal);    std::println("ocho bits: {:08b}", decimal);    std::println("hexadecimal: {:x} {:X} {:#x}", decimal, decimal, decimal);}
Salida
45 45 45binario: 101101ocho bits: 00101101hexadecimal: 2d 2D 0x2d

Las tres variables guardan el mismo valor, escrito en tres bases, y la primera línea de la salida lo muestra: la base es una propiedad de la escritura, no del número. La memoria guarda en los tres casos el mismo patrón de bits. El campo {:b} omite los ceros a la izquierda; {:08b} completa ocho cifras.