No artigo anterior — Pipeline e Barramentos — vimos como a CPU executa múltiplas instruções em paralelo e como os barramentos transportam informação entre os componentes. Mas o que é essa informação? Para o hardware, tudo são bits — tensões altas e baixas, zeros e uns. A diferença entre um caractere, um inteiro negativo e um número de ponto flutuante está exclusivamente na convenção de interpretação — na forma como acordamos que um padrão de bits deve ser lido.
Essa escolha não afeta apenas o software; ela molda o design físico do chip. A quantidade de bits que escolhemos para representar um dado dita diretamente a largura do barramento de dados, a capacidade dos registradores e a complexidade da UAL. A primeira dessas convenções que precisamos entender não lida com matemática pesada, mas com a forma como o computador aprendeu a ler e escrever conosco: o texto.
Este artigo percorre essas convenções fundamentais: os padrões para representar
texto (ASCII, Unicode, UTF-8), os três métodos para inteiros com sinal (e por
que o complemento a dois dominou), os deslocamentos de bits e o padrão IEEE 754
para números reais. Entender essas representações é essencial para compreender
por que 0.1 + 0.2 ≠ 0.3 em programação e como números negativos funcionam em
hardware.
Observação: alguns assuntos deste artigo já foram abordados em artigos anteriores aqui do site. Temos uma série de artigos sobre representação numérica que cobre os detalhes de complemento a 2, deslocamentos e ponto flutuante. Este artigo aqui serve como um resumo integrado desses conceitos, focando na perspectiva de arquitetura de computadores .
Representação de caracteres #
O primeiro problema resolvido pelos computadores não foi o cálculo — foi a comunicação: como representar letras, dígitos e símbolos como bits? Para isso, o computador usa uma codificação de caracteres (character encoding): uma convenção que associa símbolos a números.
É importante separar três ideias que às vezes aparecem misturadas:
- código de caracteres: a tabela de símbolos;
- ponto de código: o número abstrato associado a um caractere;
- codificação: a forma concreta de armazenar esse ponto de código em bytes.
| Sistema | Natureza | Observação |
|---|---|---|
| ASCII | Código de caracteres de 7 bits | 128 símbolos; base histórica do texto em sistemas modernos |
| EBCDIC | Código de caracteres de 8 bits | Usado historicamente em ambientes IBM |
| Latin-1 (ISO 8859-1) | Código de caracteres de 8 bits | Extensão para línguas ocidentais |
| Unicode | Repertório universal de pontos de código | Define os caracteres; não define sozinho como armazená-los |
| UTF-8 / UTF-16 / UTF-32 | Codificações Unicode | Formas de armazenar pontos de código em bytes |
ASCII, Latin-1 e o 8º bit #
O ASCII (American Standard Code for Information Interchange) usa 7 bits e representa 128 símbolos. Ele inclui:
- caracteres de controle (
0a31); - caracteres imprimíveis (
32a127), como letras, dígitos e pontuação.
Historicamente, em muitos sistemas o 8º bit do byte podia ser usado como bit de paridade para detectar erros de transmissão. Em outros contextos, esse bit passou a ser aproveitado para extensões de 8 bits, como o Latin-1, permitindo representar caracteres acentuados e símbolos adicionais.
Unicode e codificações UTF #
O Unicode não deve ser entendido como uma codificação fixa de 16 bits, mas
como um repertório universal de pontos de código. Seu espaço de códigos vai
de U+0000 até U+10FFFF.
Para armazenar esses pontos de código em memória ou em arquivos, usamos codificações como:
| Codificação | Comprimento | Característica principal |
|---|---|---|
| UTF-8 | 1 a 4 bytes | Compatível com ASCII nos primeiros 128 códigos |
| UTF-16 | 2 ou 4 bytes | Usa 1 unidade de 16 bits no BMP e pares substitutos fora dele |
| UTF-32 | 4 bytes | Um ponto de código por unidade de 32 bits |
Assim, o correto é pensar:
- Unicode = repertório de caracteres;
- UTF-8 / UTF-16 / UTF-32 = maneiras de codificar esse repertório.
UTF-8 domina a prática moderna
O UTF-8 se tornou a codificação dominante porque preserva total compatibilidade com ASCII nos primeiros 128 códigos e usa comprimento variável apenas quando necessário. Para arquivos de texto, protocolos e páginas web, ele é hoje a escolha padrão.
Inteiros sem sinal #
Para inteiros sem sinal (unsigned), a representação é direta: cada bit corresponde a uma potência de 2, do LSB (bit menos significativo, \(b_0\)) ao MSB (bit mais significativo):
$$N_{10} = \sum_{i=0}^{n-1} b_i \cdot 2^i$$Exemplo: \(10110101_2\):
$$= 1\cdot2^7 + 0\cdot2^6 + 1\cdot2^5 + 1\cdot2^4 + 0\cdot2^3 + 1\cdot2^2 + 0\cdot2^1 + 1\cdot2^0 = 128 + 32 + 16 + 4 + 1 = 181_{10}$$Conversão binário ↔ hexadecimal #
O hexadecimal funciona como uma notação compacta para binário: agrupe os bits de 4 em 4, da direita para a esquerda, e converta cada grupo para seu dígito hexadecimal equivalente (0–9, A–F).
| Binário | Grupos de 4 | Hexadecimal | Decimal |
|---|---|---|---|
01011010 |
0101 + 1010 |
5A |
90 |
11110011 |
1111 + 0011 |
F3 |
243 |
Verificação: \(\text{5A}_{16} = 5 \times 16 + 10 = 80 + 10 = 90_{10}\) ✓
Inteiros com sinal #
Representar números negativos exige uma convenção adicional. Ao longo da história, três métodos foram usados:
Sinal e magnitude #
O MSB indica o sinal (0 = positivo, 1 = negativo); os demais \(n-1\) bits
representam o módulo. Intervalo: \(-(2^{n-1}-1)\) a \(+(2^{n-1}-1)\). O problema
fatal: dois zeros — \(+0 = 0000\,0000\) e \(-0 = 1000\,0000\) — complicam
circuitos de comparação e aritmética.
Complemento de 1 (C1) #
O negativo de um número é obtido invertendo todos os bits. O resultado de \(-5\) em 8 bits: \(+5 = 0000\,0101 \Rightarrow -5 = 1111\,1010\). A aritmética fica mais simples do que no sinal e magnitude, mas o problema de dois zeros permanece (\(0000\,0000\) e \(1111\,1111\)). O C1 raramente é usado em sistemas modernos.
Complemento de 2 (C2) — o padrão moderno #
O negativo é obtido invertendo todos os bits e somando 1 ao resultado. O C2 tem apenas um zero, e a subtração se reduz a uma adição — o hardware da UAL precisa de apenas um somador para realizar as duas operações.
$$\text{Intervalo:} \quad -2^{n-1} \leq x \leq +(2^{n-1}-1)$$| \(n\) bits | Mínimo | Máximo |
|---|---|---|
| 8 | \(-128\) | \(+127\) |
| 16 | \(-32.768\) | \(+32.767\) |
| 32 | \(-2.147.483.648\) | \(+2.147.483.647\) |
Por que o complemento a 2 é o padrão?
Três razões concretas fazem do C2 o padrão universal:
- Subtração = adição: \(A - B = A + C2(B)\) — a UAL precisa apenas de um somador.
- Zero único: sem ambiguidade entre \(+0\) e \(-0\).
- Intervalo assimétrico: em 8 bits, vai de \(-128\) a \(+127\) — o lado negativo tem um valor a mais do que o sinal e magnitude, o que simplifica a faixa de representação.
Operações em complemento a 2 #
Como calcular o C2 de um número (negação) #
O algoritmo tem três passos:
- Escrever o número em binário
- Inverter todos os bits (obter o complemento de 1)
- Somar 1 ao resultado
| Passo | Bits |
|---|---|
| \(+5\) em binário | 0000 0101 |
| Inverter todos os bits (C1) | 1111 1010 |
| Somar 1 → resultado | 1111 1011 = \(-5\) ✓ |
Verificação (\(+5 + (-5)\) deve ser zero):
$$0000\,0101 + 1111\,1011 = \underbrace{1}_{\text{carry}}\;0000\,0000 \xrightarrow{\text{descarta carry}} 0000\,0000 = 0 \;\checkmark$$Subtração como adição: \(A - B = A + C2(B)\) #
Para entender como isso funciona na prática, vamos simular a operação 5 - 3.
Seguindo a fórmula acima, definimos \(A = 5\) e \(B = 3\). Em vez de usar um
circuito dedicado para subtrair, a CPU calcula o complemento de dois de 3
(obtendo a representação em bits de -3) e soma esse valor diretamente ao
número 5. A tabela a seguir detalha esse processo passo a passo usando 8 bits:
| Passo | Operação | Resultado |
|---|---|---|
| Representar \(+3\) | binário | 0000 0011 |
| Inverter bits (C1) | 1111 1100 |
|
| Somar 1 → \(C2(3) = -3\) | 1111 1101 |
|
| Somar \(5 + (-3)\) | 0000 0101 + 1111 1101 |
\(\underbrace{1}_{\text{carry}}\,0000\,0010\) |
| Descartar carry | 0000 0010 = \(2\) ✓ |
Exemplo — Adição, subtração e overflow em 8 bits
Considere \(A = 00110010_2 = +50_{10}\) e \(B = 11010000_2\). Para determinar o valor de B: como o MSB é 1, é negativo. \(C2(B) = 00110000_2 = 48\), portanto \(B = -48_{10}\).
a) \(A + B\): \(00110010 + 11010000 = 1\;00000010_2\) → descarta carry → \(00000010_2 = +2_{10}\) ✓ (\(50 + (-48) = +2\) — sinais opostos nunca geram overflow)
b) \(B - A = B + C2(A)\): \(C2(A)\): inverter \(00110010 = 11001101\) + 1 = \(11001110_2\) \(11010000 + 11001110 = 1\;10011110_2\) → descarta carry → \(10011110_2\) MSB=1 → negativo: \(C2(10011110) = 01100010 = 98\) → resultado = \(-98_{10}\) (\(-48 - 50 = -98\) ✓ — dentro do intervalo \([-128, +127]\))
c) Dois positivos podem somar para um resultado negativo? Sim — isso é exatamente o overflow. Exemplo: \(C = 01000001_2 = +65\) e \(D = 01100000_2 = +96\): \(C + D = 11100001_2\) → MSB=1 → interpretado como negativo, mas \(65 + 96 = 161 > 127\) → overflow ❌
Detectando overflow #
Overflow em complemento a 2
O overflow ocorre quando o resultado correto não cabe no número de bits disponíveis:
- Dois positivos somados → resultado negativo = overflow ❌
- Dois negativos somados → resultado positivo = overflow ❌
- Positivo + Negativo → nunca gera overflow ✓
Regra de hardware: overflow ocorre quando o carry de entrada no bit de sinal é diferente do carry de saída do bit de sinal.
Caso essa parte do overflow tenha sido difícil para você, temos todo um artigo dedicado a isso na série Do Zero ao Float, onde explicamos o conceito de overflow em detalhes, com exemplos e exercícios.
Deslocamentos de bits (shifts) #
Essa eficiência estrutural do Complemento a 2 não se limita apenas à facilidade de somar números negativos; ela permite que o hardware utilize “atalhos” matemáticos. A operação mais eficiente possível para multiplicar ou dividir por potências de 2 não aciona somadores complexos, mas sim os deslocamentos de bits (shifts) — uma operação resolvida “empurrando” bits para os lados em um único ciclo de UAL.
| Operação | Efeito matemático | Bit inserido |
|---|---|---|
| Deslocamento à esquerda (1 bit) | Multiplica por 2 | 0 à direita |
| Deslocamento lógico à direita (1 bit) | Divide por 2 (inteiro sem sinal) | 0 à esquerda |
| Deslocamento aritmético à direita (1 bit) | Divide por 2 (inteiro com sinal) | Réplica do MSB (bit de sinal) |
A réplica do MSB funciona da seguinte forma: se o número é positivo (MSB = 0), o
deslocamento à direita preenche com 0, mantendo o resultado positivo. Se o
número é negativo (MSB = 1), o deslocamento preenche com 1, preservando o
sinal negativo. Isso faz com que o deslocamento aritmético à direita se comporte
como uma divisão inteira por 2, arredondando para baixo, mesmo para números
negativos. Por exemplo: \(-3\) em 8 bits é 1111 1101. Deslocar à direita aritmeticamente resulta em
1111 1110, pois o MSB é 1, indicando um número negativo, e 1 é inserido à
esquerda para manter o sinal. O resultado é \(-2\), que é o resultado esperado
de \(-3\) dividido por 2, arredondando para baixo.
Exemplos em 8 bits:
| Operação | Antes | Depois | Valor |
|---|---|---|---|
| Shift esquerdo | 0000 0011 (3) |
0000 0110 |
6 = 3 × 2 ✓ |
| Shift lógico direito | 0000 0110 (6) |
0000 0011 |
3 = 6 ÷ 2 ✓ |
| Shift aritmético direito | 1111 0000 (−16) |
1111 1000 |
−8 = −16 ÷ 2 ✓ |
Deslocamento aritmético à direita: cuidado com a interpretação #
O deslocamento aritmético à direita é frequentemente apresentado como uma forma rápida de “dividir por 2” inteiros com sinal. Isso é útil como intuição, mas merece uma observação:
- para inteiros sem sinal, o deslocamento lógico à direita equivale à divisão inteira por 2;
- para inteiros com sinal em complemento a 2, o deslocamento aritmético à direita preserva o bit de sinal, o que faz o resultado se comportar como um arredondamento para baixo em muitos casos negativos.
Por isso, a frase mais segura é:
o deslocamento aritmético à direita é uma operação de hardware que, em complemento a 2, costuma corresponder à divisão inteira por potência de 2 com preservação do sinal.
Dica: aritmético × lógico
Use shift aritmético à direita para dividir inteiros com sinal
(replica o bit de sinal, preservando-o). Use shift lógico para inteiros
sem sinal (preenche com 0). Deslocar à esquerda multiplica por 2 em ambos
os casos — basta garantir que não há overflow.
Exemplo — Divisão por 2 repetida com deslocamento aritmético
Considere \(A = 01011110_2 = +94_{10}\) e \(B = 10011101_2 = -99_{10}\).
Dividindo A (positivo) por 2 com shift aritmético à direita: \(01011110 \to 00101111 = 47 \to 00010111 = 23 \to 00001011 = 11 \to 00000101 = 5 \to 00000010 = 2 \to 00000001 = 1 \to 00000000 = 0\) Cada step divide por 2 (truncando), até atingir zero.
Dividindo B (negativo) por 2: o shift aritmético replica o bit de sinal (MSB=1), preservando o sinal negativo: \(10011101 \to 11001110 = -50 \to 11100111 = -25 \to 11110011 = -13 \to 11111001 = -7 \to 11111100 = -4 \to 11111110 = -2 \to 11111111 = -1 \to 11111111 = -1\) Para números negativos, o processo converge para −1 (não para zero), pois o bit de sinal nunca é eliminado.
Ponto flutuante: o padrão IEEE 754 #
Embora os inteiros em Complemento a 2 sejam perfeitos para contagens exatas e manipulação de memória, o mundo físico — com suas distâncias microscópicas e grandezas astronômicas — exige uma flexibilidade que os bits fixos não conseguem oferecer. Para grandezas contínuas, precisamos de números reais aproximados. O ponto flutuante resolve isso utilizando uma notação científica normalizada em base 2:
$$(-1)^S \times 1{,}\text{mantissa} \times 2^E$$onde \(S\) é o bit de sinal (0 = positivo, 1 = negativo), \(E\) é o expoente inteiro e a mantissa são os bits fracionários após o 1. implícito.
Mas de onde vêm as regras para guardar isso em bits? Historicamente, cada fabricante de computador criava seu próprio jeito de representar números de ponto flutuante, o que causava um caos: um mesmo cálculo científico dava resultados diferentes dependendo de qual marca de computador o executasse. Para unificar a indústria, o IEEE (Institute of Electrical and Electronics Engineers — a principal organização mundial de padronização para engenharia elétrica e computação) publicou em 1985 a norma IEEE 754.
O padrão IEEE 754 original definiu com exatidão os formatos básicos de precisão simples e dupla, estabelecendo apenas requisitos mínimos para formatos “estendidos”. A tabela a seguir mostra os formatos de 32 e 64 bits definidos na norma, além do famoso formato extended de 80 bits (uma implementação específica da Intel/x86 que atende aos requisitos de formato estendido da norma):
| Precisão | Sinal | Expoente | Mantissa | Total | Bias |
|---|---|---|---|---|---|
| Single (32 bits) | 1 bit | 8 bits | 23 bits | 32 bits | 127 |
| Double (64 bits) | 1 bit | 11 bits | 52 bits | 64 bits | 1.023 |
| Extended (80 bits) | 1 bit | 15 bits | 64 bits | 80 bits | 16.383 |
O expoente é armazenado com bias (viés): o valor real é o armazenado menos
o bias, permitindo representar expoentes negativos sem bit de sinal próprio. O
bit 1 à esquerda do ponto é implícito no single e double — não precisa ser
armazenado, adicionando um bit extra de precisão efetiva (24 bits no single, 53
bits no double). Já no formato extended (80 bits), esse bit inteiro inicial é
explícito e precisa ser armazenado, ocupando um espaço real e resultando em
exatos 64 bits de precisão na mantissa. Hoje em dia, o formato extended de 80
bits é amplamente considerado legado: ele era o padrão interno dos antigos
coprocessadores matemáticos (x87 FPU) da arquitetura x86. Processadores modernos
geralmente realizam cálculos de ponto flutuante usando registradores vetoriais
(SSE/AVX) otimizados para os formatos single e double. Contudo, o extended
ainda é suportado nos processadores atuais por questões de retrocompatibilidade
(sendo frequentemente mapeado para o tipo long double em compiladores C/C++ na
arquitetura x86).
Abaixo, a estrutura de bits para o formato de precisão simples (32 bits) do IEEE 754, onde o bit de sinal é o mais significativo (MSB), seguido pelo expoente com bias e pela mantissa fracionária:
31 30 23 22 0
┌───┬──────────┬──────────────────────────┐
│ S │ Expoente │ Mantissa │
│ 1 │ 8 bits │ 23 bits │
└───┴──────────┴──────────────────────────┘Casos especiais #
O IEEE 754 reserva padrões específicos para situações limite:
| Expoente | Mantissa | Significado |
|---|---|---|
00000000 |
000...0 |
Zero (\(\pm 0\)) |
00000000 |
\(\neq 0\) | Número denormalizado (subnormal) |
11111111 |
000...0 |
\(\pm\infty\) |
11111111 |
\(\neq 0\) | NaN (Not a Number) |
O vácuo perto do zero e o Underflow
Se você observar a matemática do IEEE 754, perceberá que existe um “salto” de
precisão considerável entre o menor número normalizado possível e o zero
absoluto. Para evitar que cálculos percam a precisão abruptamente e desabem
para zero ao lidar com valores atômicos (um problema conhecido como
underflow), o padrão introduziu os números denormalizados. Eles abrem
mão do 1. implícito na mantissa para conseguir representar valores
absurdamente pequenos (como \(1,4 \times 10^{-45}\) na precisão simples),
preenchendo esse “vácuo” com uma degradação suave de precisão.
Convertendo para IEEE 754 single precision #
flowchart TD
A["Número decimal com sinal"] --> B["1. Converter para binário"]
B --> C["2. Normalizar: 1.xxx × 2^E"]
C --> D["3. Bit de sinal S
0 = positivo, 1 = negativo"]
C --> Exp["4. Expoente com bias
E_real + 127"]
C --> Mant["5. Mantissa
bits após o 1 implícito
(completar com zeros à direita)"]
D --> R["Resultado: S | Expoente | Mantissa"]
Exp --> R
Mant --> R
Aplicando ao exemplo \(-5{,}75\):
| Passo | Operação | Resultado |
|---|---|---|
| 1. Converter para binário | \(5 = 101_2\); \(0{,}75 \times 2 = 1{,}50\) → 1; \(0{,}5 \times 2 = 1{,}0\) → 1 | \(-101{,}11_2\) |
| 2. Normalizar | Mover ponto 2 posições à esquerda | \(-1{,}0111 \times 2^2\) |
| 3. Bit de sinal | Negativo | 1 |
| 4. Expoente com bias | \(E_{\text{real}} = 2\); \(2 + 127 = 129\) | \(10000001_2\) |
| 5. Mantissa | Bits após 1.: 0111, completar com zeros |
01110000000000000000000 |
| Resultado final | 1 10000001 01110000000000000000000 |
Verificação:
$$(-1)^1 \times 1{,}0111_2 \times 2^{129-127} = -1{,}0111_2 \times 2^2 = -101{,}11_2 = -5{,}75_{10} \;\checkmark$$
Exemplo — Decodificando um formato personalizado de 16 bits
Um formato hipotético usa: 1 bit de sinal, 5 bits de expoente com bias 15, e 10 bits de mantissa (normalizado — parte inteira implícita = 1).
Dado o padrão BA32₁₆ = 1011 1010 0011 0010₂:
- Sinal:
1→ negativo - Expoente:
01110₂ = 14→ \(E_{\text{real}} = 14 - 15 = -1\) - Mantissa:
1000110010₂
Valor: \(-(1{,}1000110010_2) \times 2^{-1}\)
Convertendo a mantissa: \(1 + 2^{-1} + 2^{-5} + 2^{-6} + 2^{-9} = 1 + 0{,}5 + 0{,}03125 + 0{,}015625 + 0{,}001953\ldots \approx 1{,}5488\)
Resultado: \(-1{,}5488 \times 0{,}5 \approx -0{,}7744\)
Maior valor positivo normalizado: expoente = 11110₂ = 30 → \(E = 15\); mantissa = 1111111111₂ → \(1{,}1111111111_2 \times 2^{15} = 65504\)
Menor valor positivo normalizado: expoente = 00001₂ = 1 → \(E = -14\); mantissa = 0000000000₂ → \(1{,}0 \times 2^{-14} \approx 0{,}000061\)
Exemplo — Decodificando um número IEEE 754
Número com: sinal = 0 (positivo), mantissa = 1100100₂, expoente real = −3.
Número normalizado: \(+(1{,}1100100)_2 \times 2^{-3}\)
Convertendo a mantissa: \(1 + 2^{-1} + 2^{-2} + 2^{-5} = 1 + 0{,}5 + 0{,}25 + 0{,}03125 = 1{,}78125\)
Resultado: \(1{,}78125 \times 2^{-3} = 1{,}78125 / 8 = \mathbf{0{,}222656}\)
Aprofundamento: a série “Do Zero ao Float”
O padrão IEEE 754 tem muitas nuances além do que cobrimos aqui:
arredondamento, números subnormais, operações especiais e as armadilhas de
comparar floats. Se você quiser explorar esses tópicos em profundidade —
incluindo por que 0.1 + 0.2 ≠ 0.3 em praticamente toda linguagem de
programação — a série Do Zero ao Float
cobre tudo isso com diversos exemplos.
Bônus Python: inspecionando representações de bits #
Python oferece ferramentas diretas para inspecionar como números são armazenados
em bits, usando o módulo struct (para ponto flutuante) e os operadores << e
>> (para inteiros).
import struct
# Representação IEEE 754 single precision de -5.75
raw = struct.pack(">f", -5.75) # ">f" = big-endian single float
print(f"-5.75 como bytes : {raw.hex()}")
# c0b80000
bits = int.from_bytes(raw, "big")
print(f"Em binário (32) : {bits:032b}")
# 11000000101110000000000000000000
# S=1 | Exp=10000001 | Mant=01110000000000000000000 ✓
# Desempacotando de volta
valor = struct.unpack(">f", raw)[0]
print(f"De volta ao float: {valor}")
# -5.75 ✓
# Shifts e complemento a 2 com inteiros Python
a = 0b00110010 # +50 em decimal
b = 0b11010000 # em 8 bits sem sinal = 208, em C2 = -48
print(f"\na = {a} ({a:08b})")
print(f"a << 1 = {a << 1} ({a<<1:08b}) (× 2)")
print(f"a >> 1 = {a >> 1} ({a>>1:08b}) (÷ 2)")
# Complemento a 2 em Python (Python usa inteiros de precisão arbitrária)
def c2_negacao(n: int, bits: int = 8) -> int:
"""Retorna -n em representação complemento a 2 com 'bits' bits."""
mascara = (1 << bits) - 1 # 0xFF para 8 bits
return (~n & mascara) + 1
print(f"\n-50 em C2 (8 bits) : {c2_negacao(50):08b} = {c2_negacao(50)}")
# 11001110 = 206 (em unsigned) ou -50 (em C2)
# Adição com truncamento para 8 bits
def soma_c2(a: int, b: int, bits: int = 8) -> int:
mascara = (1 << bits) - 1
return (a + b) & mascara
print(f"50 + (-48) em 8 bits: {soma_c2(a, b):08b} = {soma_c2(a, b)}")
# 00000010 = 2 ✓
# Verificando -5.75 como double (64 bits)
raw64 = struct.pack(">d", -5.75)
bits64 = int.from_bytes(raw64, "big")
print(f"\n-5.75 double (64 bits): {bits64:064b}")
# 1 10000000001 0111000000000000000000000000000000000000000000000000Conclusão e próximos artigos #
Bits sozinhos não têm significado — o significado emerge da convenção de interpretação. O ASCII transforma 7 bits em um caractere; o complemento a dois transforma um padrão de bits em um inteiro negativo; o IEEE 754 transforma 32 bits em um número fracionário de precisão. Entender essas convenções é entender como o computador funciona.
No próximo artigo, vamos explorar a representação de instruções de máquina — como os padrões de bits que compõem as instruções que a CPU executa, os diferentes formatos de instrução e os modos de endereçamento que determinam onde buscar os operandos. Compreender a representação de dados é o passo fundamental para entender a representação de instruções, pois as instruções também são apenas padrões de bits, mas com uma estrutura específica que o hardware interpreta para realizar operações.
Até lá!