Números Binários

Manipulação de Bits10 min de leituraFácilPython

Binário não é um código secreto que o computador usa para esconder as coisas. É o mesmo sistema de numeração que você já usa desde a escola, com um parâmetro trocado: em vez de dez símbolos por posição, dois. Entender essa frase inteira é entender o tópico, e o resto do artigo é ela em detalhe.

Dois símbolos, e o motivo é físico

O sistema que usamos no dia a dia se chama decimal porque tem dez símbolos: 0, 1, 2, 3, 4, 5, 6, 7, 8 e 9. Não há nada de especial neles além de termos dez dedos.

O computador usa binário, com dois símbolos: 0 e 1. Cada um deles é um bit, e um conjunto de oito bits é um byte.

A escolha não foi estética, foi elétrica. Por dentro, um computador é um monte de circuitos por onde passa (ou não passa) corrente. Distinguir "tem corrente" de "não tem corrente" é trivial e resiste a ruído, variação de temperatura e componente envelhecido. Distinguir dez níveis diferentes de tensão para representar dez símbolos seria possível e péssimo: qualquer oscilação transformaria um 6 num 7, e o computador precisaria de circuitos de correção em cada ponto.

Dois estados é o mínimo que ainda carrega informação, e por isso é o mais robusto. Todo o resto (números, texto, imagem, som, este parágrafo) é construído em cima dessa decisão.

Notação posicional: você já sabe fazer isso

Pegue o número decimal 243. O que ele significa, na matemática?

243 = 2 x 10²  +  4 x 10¹  +  3 x 10⁰
    = 2 x 100  +  4 x 10   +  3 x 1
    = 200      +  40       +  3

Repare na estrutura: cada dígito é multiplicado por uma potência da base, e o expoente é a posição, contada da direita para a esquerda começando em zero. O dígito é o coeficiente, o 10 é a base, e a posição é o expoente.

A base é a única coisa que muda entre sistemas de numeração. Ela diz quantos símbolos existem por posição, e é ela que aparece na potência:

SistemaBaseSímbolos
Binário20 e 1
Octal80 a 7
Decimal100 a 9
Hexadecimal160 a 9 e A a F

Ou seja, não existem quatro regras diferentes para aprender. Existe uma regra, com um parâmetro.

Lendo um binário: a soma das posições ligadas

Aplicando a mesma fórmula com base 2, o binário 00110101 vale:

0 x 2⁷ + 0 x 2⁶ + 1 x 2⁵ + 1 x 2⁴ + 0 x 2³ + 1 x 2² + 0 x 2¹ + 1 x 2⁰
=   0  +   0    +  32    +  16    +   0    +   4    +   0    +   1     = 53

Como os únicos coeficientes possíveis são 0 e 1, a multiplicação some da conta: os bits em zero contribuem com nada, e os bits em um contribuem com a potência inteira. Ler um binário é somar as potências de dois das posições ligadas. Nada além disso.

Visualizador · o binário é uma soma de potências de dois
00110101 = 53

Um número qualquer. Repare que os únicos termos que entram na soma são os dos bits ligados: os zeros multiplicam a potência por zero e somem da conta, e é literalmente por isso que eles não valem nada.

Os oito bits clique para ligar e desligar

O bit da esquerda é o mais significativo e o da direita o menos significativo, exatamente como a centena e a unidade num número decimal.

A conta, escrita por extenso os bits desligados multiplicam por zero e somem

1 x 25 + 1 x 24 + 1 x 22 + 1 x 20

32 + 16 + 4 + 1 = 53

valor decimal53
bits ligados4
combinações com 8 bits256
maior valor possível255

A mesma ideia vale no decimal: 243 é 2 x 10² + 4 x 10¹ + 3 x 10⁰. Muda a base, e com ela quantos símbolos existem por posição. É por isso que aprender binário não é aprender um sistema novo, é reconhecer o sistema que você já usa com outra base. Ligue e desligue o bit da esquerda para ver o número saltar 128 de uma vez: essa é a diferença entre a posição mais significativa e a menos, e é a mesma diferença entre a centena e a unidade.

Vale fixar o vocabulário, porque ele aparece o tempo todo: o bit da esquerda é o mais significativo (MSB, de most significant bit) e o da direita é o menos significativo (LSB). É a mesma ideia da centena e da unidade num número decimal: mexer no primeiro muda muito, mexer no último muda pouco.

Escrevendo em binário: dividir por 2 até acabar

O caminho de volta é um algoritmo de três linhas: divida o número por 2, guarde o resto, repita com o quociente até chegar a zero, e leia os restos de trás para frente.

Python
def para_binario(n):
    if n == 0: return '0'
    bits = ''
    while n > 0:
        bits = str(n % 2) + bits   # o resto entra na FRENTE
        n = n // 2                 # e o número encolhe pela metade
    return bits
Visualizador · de decimal para binário, dividindo por 2
passo 1 de 10

Oito divisões para oito bits. Acompanhe a fita da direita: o primeiro resto ocupa a última posição, e não a primeira. É esse detalhe que faz a regra de ler os restos de baixo para cima.

A fita de bits preenchida da direita para a esquerda, um por divisão
?27128?2664?2532?2416?238?224?212?201
As divisões o quociente vira a próxima linha, o resto vira um bit
  1. nenhuma divisão ainda

Vou converter 201 para binário dividindo por 2 sem parar. Cada divisão responde a uma pergunta só: "sobra alguma coisa?". O resto é o bit, e o quociente é o que ainda falta converter.

para_binario.py
1def para_binario(n):
2 if n == 0: return '0'
3 bits = ''
4 while n > 0:
5 bits = str(n % 2) + bits # o resto entra na FRENTE
6 n = n // 2 # e o número encolhe pela metade
7 return bits
Variáveis
n (o que falta converter)201
último resto-
bits já escritos0
número de partida201
divisões feitas0
bits significativos8
bits já ligados na fita0

O número de divisões é o número de bits significativos, e ele cresce como log₂ do valor: 201 precisa de 8 divisões, e 201 mil precisaria de 18. É a mesma razão pela qual a busca binária é barata, escrita de outro jeito: dividir por dois repetidamente chega ao fim depressa.

passo · espaço roda

Duas observações que transformam a receita em entendimento.

A primeira é por que os restos saem ao contrário. Dividir por 2 é deslocar o número inteiro uma casa para a direita, e o resto é justamente o bit que cai fora nesse deslocamento. Ou seja, a primeira divisão devolve o bit menos significativo. Não é um truque de leitura, é a ordem em que a informação aparece.

A segunda é que o resto da divisão por 2 é exatamente a pergunta "este número é ímpar?". Todo número ímpar tem o bit da direita ligado, todo par tem ele desligado, e é por isso que n & 1 é a forma idiomática de testar paridade em código de baixo nível.

O número de divisões é o número de bits significativos, e ele cresce como log₂ n. O 201 precisa de 8 divisões; 201 mil precisaria de 18; 201 bilhões, de 38. É a mesma razão pela qual a busca binária é barata, escrita de outro jeito: partir pela metade repetidamente chega ao fim depressa.

Bit, byte, e quanto cabe

Com k bits existem 2ᵏ combinações diferentes. Um byte tem 8 bits, então são 2⁸ = 256 combinações, e os valores vão de 0 a 255.

O 255 costuma causar confusão, e a conta explica: 128 + 64 + 32 + 16 + 8 + 4 + 2 + 1 = 255. Somar todas as potências de dois até 2ⁿ⁻¹ sempre dá 2ⁿ - 1, um a menos que a próxima potência. São 256 valores porque o zero também conta.

Visualizador · a base é um parâmetro, e os bits são um orçamento
255 = 0xFF = 0b11111111

O mesmo valor escrito em quatro sistemas. Nenhuma das quatro escritas é "o número": o número é a quantidade, e cada linha é uma forma de anotá-la. O que muda de base para base é quantos símbolos existem por posição, e por consequência quantas posições são necessárias.

O mesmo 255 em quatro bases quanto menor a base, mais dígitos
baseescritadígitospor que ela existe
binário (2)
111111118
2 símbolos: 0 e 1. É o que existe no hardware.
octal (8)
3773
8 símbolos. Cada dígito vale exatamente 3 bits. Sobrevive nas permissões de arquivo do Unix.
decimal (10)
2553
10 símbolos. É o único da lista que não tem relação com potências de dois.
hexadecimal (16)
FF2
16 símbolos, de 0 a F. Cada dígito vale exatamente 4 bits, e é por isso que ele é a forma curta de escrever binário.
Por que hexadecimal e não decimal cada dígito hexa é um grupo de 4 bits, sem sobra
1111F1111F

Com decimal essa correspondência não existe: 10 não é potência de 2, então um dígito decimal não corresponde a um número inteiro de bits e converter exige dividir. Com hexadecimal, converter é recortar de quatro em quatro.

Quantos bits, quanto cabe a conta é sempre 2 elevado ao número de bits
tamanhocombinaçõesmaior valor sem sinalonde aparece
byte
8 bits
2^8255
byte, char, uint8
16 bits
16 bits
2^1665.535
short, uint16
32 bits
32 bits
2^324 bi
int, float, cor RGBA
64 bits
64 bits
2^6418 qui
long, double, ponteiro

Os 255 escolhidos precisam de 8 bits para serem escritos, ou 2 dígitos hexadecimais. Repare que dobrar a quantidade de bits não dobra o alcance, ele eleva ao quadrado: 8 bits vão até 255, 16 até 65.535, e 32 até mais de 4 bilhões. É a mesma curva exponencial de sempre, vista do lado de dentro.

O salto de 32 para 64 bits é o que mais aparece na prática. Um inteiro de 32 bits com sinal vai até 2.147.483.647, e isso é pouco para coisas comuns: contar milissegundos desde 1970 estoura em 2038, e somar centavos de uma empresa grande estoura antes disso. Guardar identificador, dinheiro ou tempo em 32 bits é uma decisão, não um detalhe.

Essa conta é o orçamento de qualquer tipo numérico:

TamanhoCombinaçõesMaior valor sem sinal
8 bits (byte)2⁸ = 256255
16 bits2¹⁶ = 65.53665.535
32 bits2³² ≈ 4,3 bilhões4.294.967.295
64 bits2⁶⁴ ≈ 18,4 quintilhões18.446.744.073.709.551.615

E o salto entre as linhas é exponencial, não linear: dobrar a quantidade de bits eleva o alcance ao quadrado. É por isso que a diferença entre 32 e 64 bits importa tanto na prática, e é por isso que quase nada mais é declarado com 32 bits quando se trata de identificador, dinheiro ou tempo.

Hexadecimal: binário com outra roupa

Ninguém quer escrever 11011110101011011011111011101111 num código. Por outro lado, o valor decimal correspondente (3.735.928.559) esconde completamente quais bits estão ligados.

O hexadecimal resolve os dois problemas porque 16 é uma potência de 2. Precisamente, 16 = 2⁴, então cada dígito hexadecimal corresponde a exatamente 4 bits, sem sobra e sem depender dos vizinhos:

BitsHexaBitsHexa
0000010008
0001110019
001021010A
001131011B
010041100C
010151101D
011061110E
011171111F

Converter passa a ser recortar de quatro em quatro, sem nenhuma conta: 11110011 vira 1111 0011, que vira F3. E 0xF3 é 243, o mesmo número da seção sobre notação posicional.

Com decimal essa correspondência não existe, porque 10 não é potência de 2. Um dígito decimal ocupa entre 3 e 4 bits e não se alinha com nada, então converter exige dividir. É essa a razão inteira de endereço de memória, cor, máscara de bits e dump de arquivo serem escritos em hexadecimal: não é tradição, é alinhamento.

Onde isso aparece no código do dia a dia

A pergunta justa é o que fazer com isto num trabalho em que ninguém pede para converter números na mão. Quatro coisas, todas comuns:

Cor

#FF8000 é RGB com um byte por canal: FF = 255 de vermelho, 80 = 128 de verde, 00 = 0 de azul. Três bytes, um valor de 0 a 255 cada.

Permissão

O chmod 755 do Unix é octal: cada dígito são 3 bits (leitura, escrita, execução) para dono, grupo e outros. 7 é 111, 5 é 101.

Flags

Guardar dezenas de opções booleanas num inteiro só, uma por bit, e testar com máscaras. Cabe num campo de banco e viaja de graça na rede.

Limite

Todo int tem um teto. Saber que 32 bits param em 2,1 bilhões com sinal é o que evita descobrir isso em produção.

O caso mais famoso do último cartão tem data marcada: o tempo em sistemas Unix é contado em segundos desde 1 de janeiro de 1970, e por muito tempo isso foi guardado num inteiro de 32 bits com sinal. Esse contador estoura em 19 de janeiro de 2038, e sistemas que ainda usarem 32 bits nessa data vão passar a ver uma data em 1901. O conserto (mudar para 64 bits) é conhecido há décadas e ainda está sendo aplicado, o que diz bastante sobre quanto uma decisão de representação sobrevive.

Daqui, o próximo passo é entender como o mesmo conjunto de bits representa números negativos, que é um problema mais interessante do que parece: existem três convenções conhecidas, duas delas têm defeito, e a vencedora usa um truque que faz o processador subtrair sem saber subtrair. Está em Binários Negativos. Depois disso, Operações Bitwise mostra o que dá para fazer manipulando esses bits diretamente, e vale ver como os mesmos bytes viram texto em Strings.

Vídeo da aula

Direto do canal da comunidade Craft & Code Club · 27:33.

Problemas para praticar

Na ordem em que recomendamos resolver. Marque os que você já fez, fica salvo aqui.

Referências

Artigos e materiais externos para se aprofundar.

Travou em algum passo? Traga sua questão para o Discord da comunidade ou para os encontros semanais.

Entrar
Concluiu este tópico?
Marque para acompanhar seu progresso.

Este tópico faz parte de

Ver todos →

Números Binários aparece num percurso com objetivo próprio. O conteúdo é o mesmo; o que muda é a pergunta que ele responde ali, e o que vem antes e depois.