Resumen
Un entero del programa no es un entero de la matemática: es una sucesión finita de bits, y con bits caben exactamente 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 ; 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
- Convertir enteros entre las bases 10, 2 y 16, y comprobar la conversión con los campos de formato
{:b}y{:x}. - Calcular el rango de un entero de bits con signo y sin signo, y comprobarlo con
std::numeric_limitsysizeof. - Explicar el complemento a dos y escribir la representación en ocho bits de un negativo pequeño.
- Distinguir el desbordamiento con signo, que es comportamiento indefinido, del desbordamiento sin signo, que es aritmética módulo , y predecir el resultado del segundo.
- 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 , cada cifra vale según la posición que ocupa: la última multiplica a , la anterior a , la anterior a , y así sucesivamente. En base 10, significa . En base 2 las cifras son 0 y 1, y los pesos son las potencias de 2: De ello se sigue la regla para pasar de binario a decimal: se suman los pesos de las posiciones donde hay un 1.
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
Ocho casillas, todas apagadas: el valor es 0. Los pesos, de derecha a izquierda, son .
El primer uno está en la posición de peso 32: el valor acumulado es 32.
El siguiente, en la de peso 8: .
El siguiente, en la de peso 4: .
El último, en la de peso 1: . El patrón
00101101representa el número 45.Agrupados de cuatro en cuatro, los bits dan las cifras hexadecimales:
0010es 2 y1101es 13, que en base 16 se escribed. Por tanto, 45 se escribe2den 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: , , , , , ; 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 , 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.
#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);}45 45 45binario: 101101ocho bits: 00101101hexadecimal: 2d 2D 0x2dLas 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.
Cargando el contenido…