Resumo
Um inteiro do programa não é um inteiro da matemática: é uma sequência finita de bits, e com bits cabem exatamente 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 ; 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
- Converter inteiros entre as bases 10, 2 e 16, e conferir a conversão com as especificações de formato
{:b}e{:x}. - Calcular o intervalo de um inteiro de bits com sinal e sem sinal, e conferi-lo com
std::numeric_limitsesizeof. - Explicar o complemento de dois e escrever a representação em oito bits de um negativo pequeno.
- Distinguir o estouro com sinal, que é comportamento indefinido, do estouro sem sinal, que é aritmética módulo , e prever o resultado do segundo.
- 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 , cada algarismo vale conforme a posição que ocupa: o último multiplica , o anterior , o anterior a este , e assim por diante. Em base 10, significa . Em base 2 os algarismos são 0 e 1, e os pesos são as potências de 2: Daí se segue a regra para passar de binário a decimal: somam-se os pesos das posições onde há um 1.
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
Oito caixas, todas apagadas: o valor é 0. Os pesos, da direita para a esquerda, são .
O primeiro um está na posição de peso 32: o valor acumulado é 32.
O seguinte, na de peso 8: .
O seguinte, na de peso 4: .
O último, na de peso 1: . A configuração
00101101representa o número 45.Agrupados de quatro em quatro, os bits dão os algarismos hexadecimais:
0010é 2 e1101é 13, que em base 16 se escreved. Portanto, 45 se escreve2dem 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: , , , , , ; 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 , 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.
#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);}45 45 45binário: 101101oito bits: 00101101hexadecimal: 2d 2D 0x2dAs 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.
Carregando o conteúdo…