Saltar para o conteúdo
Topos Uranos

Resumo

Um inteiro do programa não é um inteiro da matemática: é uma sequência finita de bits, e com nn bits cabem exatamente 2n2^n valores distintos. Esta aula estuda essa representação e suas consequências. Ensina a escrever um inteiro em base 2 e em base 16 e a conferi-lo com as especificações de formato {:b} e {:x}; apresenta os tipos inteiros de C++, seu tamanho, que se mede com sizeof, e seu intervalo, que se consulta com std::numeric_limits; explica como se representam os negativos por meio do complemento de dois e por que o intervalo não é simétrico; distingue os inteiros com sinal dos inteiros sem sinal, e estuda o que acontece quando uma operação sai do intervalo: nos sem sinal, o padrão define o resultado, que é aritmética módulo 2n2^n; nos com sinal, não o define, e o programa fica fora de toda garantia. Termina com um critério para escolher o tipo de cada dado.

Objetivos de aprendizagem

  1. Converter inteiros entre as bases 10, 2 e 16, e conferir a conversão com as especificações de formato {:b} e {:x}.
  2. Calcular o intervalo de um inteiro de nn bits com sinal e sem sinal, e conferi-lo com std::numeric_limits e sizeof.
  3. Explicar o complemento de dois e escrever a representação em oito bits de um negativo pequeno.
  4. Distinguir o estouro com sinal, que é comportamento indefinido, do estouro sem sinal, que é aritmética módulo 2n2^n, e prever o resultado do segundo.
  5. Escolher o tipo inteiro adequado para um dado segundo seu intervalo previsível, usando os tipos de largura fixa de <cstdint> quando a largura importa.

Sistemas posicionais

A aula Variáveis e memória apresentou a variável como uma caixa com nome, tipo e valor. Ora, a memória não guarda algarismos decimais: guarda bits, cada um dos quais vale 0 ou 1, agrupados em bytes de oito bits (o padrão exige pelo menos oito; todos os sistemas atuais usam exatamente oito). Para saber que valores uma variável inteira pode assumir é preciso saber, portanto, como se escreve um número com apenas dois algarismos, e é isso que faz um sistema posicional de base 2.

Em um sistema posicional de base bb, cada algarismo vale conforme a posição que ocupa: o último multiplica b0=1b^0 = 1, o anterior b1b^1, o anterior a este b2b^2, e assim por diante. Em 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. Em base 2 os algarismos são 0 e 1, e os pesos são as potências de 2: 1,2,4,8,16,32,64,128,…1, 2, 4, 8, 16, 32, 64, 128, \ldots Daí se segue a regra para passar de binário a decimal: somam-se os pesos das posições onde há um 1.

ExemploDe binário a decimal

A figura lê a configuração de oito bits 00101101. Sobre cada caixa está o seu peso; os uns se acendem um a um, da esquerda para a direita, e o valor acumula seus pesos.

Demonstração

  1. Oito caixas, todas apagadas: o valor é 0. Os pesos, da direita para a esquerda, são 1,2,4,…,1281, 2, 4, \ldots, 128.

  2. O primeiro um está na posição de peso 32: o valor acumulado é 32.

  3. O seguinte, na de peso 8: 32+8=4032 + 8 = 40.

  4. O seguinte, na de peso 4: 40+4=4440 + 4 = 44.

  5. O último, na de peso 1: 44+1=4544 + 1 = 45. A configuração 00101101 representa o número 45.

  6. Agrupados de quatro em quatro, os bits dão os algarismos hexadecimais: 0010 é 2 e 1101 é 13, que em base 16 se escreve d. Portanto, 45 se escreve 2d em base 16.

O caminho inverso, de decimal a binário, percorre-se por divisões sucessivas: divide-se o número por 2, anota-se o resto (0 ou 1) e repete-se com o quociente até chegar a 0; os restos, lidos do último ao primeiro, são os algarismos binários. 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; lidos ao contrário, os restos dão 101101, que com dois zeros à esquerda é a configuração da figura. A razão é que cada resto é o algarismo das unidades do número que se divide, e cada quociente é esse número deslocado uma posição para a direita. O mesmo método vale para qualquer base: para a base 16 divide-se por 16, e os restos de 10 a 15 se escrevem com as letras a a f.

A base 16, ou hexadecimal, existe por comodidade: como 16=2416 = 2^4, cada algarismo hexadecimal corresponde exatamente a quatro bits, e um byte se escreve com dois algarismos. Um número de 32 bits ocupa trinta e dois algarismos em binário, que se leem mal, e apenas oito em hexadecimal, que se convertem a binário sem cálculo, algarismo por algarismo. Por isso os programadores leem a memória em hexadecimal. O fundamento matemático dos sistemas posicionais se estuda no curso Álgebra e Geometria I, na aula “Sistemas de numeração posicional”.

C++ admite inteiros escritos diretamente nessas bases: um literal que começa com 0b está em binário, e um que começa com 0x, em hexadecimal. Dentro de qualquer literal pode-se pôr um apóstrofo como separador de dígitos, que o compilador ignora e o leitor agradece: 0b0010'1101, 1'000'000. Na saída, a especificação de formato {:b} escreve um inteiro em binário, {:x} em hexadecimal com minúsculas e {:X} com maiúsculas; o sinal # antepõe o prefixo, e uma largura precedida de um zero preenche com zeros à esquerda.

ExemploUm mesmo número, três escritas
Programa em 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("binário: {:b}", decimal);    std::println("oito bits: {:08b}", decimal);    std::println("hexadecimal: {:x} {:X} {:#x}", decimal, decimal, decimal);}
Saída
45 45 45binário: 101101oito bits: 00101101hexadecimal: 2d 2D 0x2d

As três variáveis guardam o mesmo valor, escrito em três bases, e a primeira linha da saída o mostra: a base é uma propriedade da escrita, não do número. A memória guarda nos três casos a mesma configuração de bits. A especificação {:b} omite os zeros à esquerda; {:08b} completa oito algarismos.