Pular para o conteúdo principal

Sistemas de Numeração e Tipos

Conceito

A partir do momento em que começamos a programar, lidamos com números — seja para contar, medir ou representar dados. Mas como os computadores entendem esses números? E quais tipos de números existem?

A RAM só entende dois estados, que podem ser entendidos como: ligado (1) e desligado (0). A partir disso, todos os tipos de dados são construídos. Pensando nessa estrutura base de 0s e 1s, vamos ver o que molda todos os tipos de dados:

  • O Bit: é a menor unidade de informação, representando um único dígito binário (0 ou 1).
  • O Byte: é composto por 8 bits e pode representar 256 valores diferentes (de 0 a 255), onde 256 representa 2^8, vale ressaltar que originalmente o ASCII usava apenas 7 bits(128 caracteres), O 8º bit foi usado posteriormente para extensões, o que permitiu codificar o alfabeto ocidental inteiro básico (ASCII).

Sistemas de Numeração

É importante entender os sistemas de numeração, pois eles são a base para a representação de dados em um computador. E é mais importante ainda que um programador não se assuste ao ver um 0xAB ou um 0b1010.

  • Decimal: a base que os humanos usam no dia a dia, composta por 10 dígitos (0–9). Cada posição representa uma potência de 10.
  • Binário: a base dos computadores, composta por 2 dígitos (0 e 1). Cada posição representa uma potência de 2. Em código, números binários são prefixados com 0b — por exemplo, 0b1010 = 10 em decimal.
  • Hexadecimal: uma base mais compacta, composta por 16 dígitos, onde cada dígito hex representa 4 bits. Os dígitos vão de 0 a 9 e depois de A a F (onde A=10, B=11, ..., F=15). Em código, são prefixados com 0x — por exemplo, 0xAB = 171 em decimal.

Como ler um número hexadecimal

0xAB não é um código secreto — é só uma forma compacta de escrever um número:

0xAB
A = 10 → 10 × 16¹ = 160
B = 11 → 11 × 16⁰ = 11
total = 171

E o mesmo número em binário:

0b10101011
1×2⁷ + 0×2⁶ + 1×2⁵ + 0×2⁴ + 1×2³ + 0×2² + 1×2¹ + 1×2⁰
= 128 + 0 + 32 + 0 + 8 + 0 + 2 + 1
= 171

Os três sistemas representam o mesmo valor — só mudam a notação. Hex é especialmente útil porque 2 dígitos hex representam exatamente 1 byte, tornando endereços de memória como 0xFF04 muito mais legíveis do que sua versão binária de 16 dígitos.

// conversor de bases

0b10101011 — grupos de 4 bits = 1 dígito hex

10101011A10B11= 0xAB

uso de memória

0
0
0
0
0
0
0
0
1
0
1
0
1
0
1
1
8 bits / 1 byte

Como uma linguagem de programação lida com números?

Uma linguagem de programação tem tipos de dados específicos para representar números, e cada tipo tem suas próprias características — o intervalo de valores que pode representar e a quantidade de memória que ocupa.

Esses dados normalmente seguem o padrão IEEE 754, que define como os números de ponto flutuante são representados. Ele divide os bits disponíveis em três partes:

  • Sinal (1 bit): positivo ou negativo
  • Expoente (8 ou 11 bits): a magnitude do número
  • Mantissa (23 ou 52 bits): a precisão — os dígitos significativos

Isso explica por que 0.1 + 0.2 !== 0.3 em JavaScript — o número 0.1 não tem representação binária exata, assim como 1/3 não tem representação decimal exata. Isso ocorre porque $0.1$ em binário é uma dízima periódica (como $1/3$ em decimal), forçando o computador a arredondar o valor e gerando essa pequena diferença.

Tipos de dados essenciais

  • Booleanos (Boolean): representam valores de verdade (true ou false). Conceitualmente ocupam 1 bit, mas na prática a maioria das linguagens aloca 1 byte por questões de alinhamento de memória.

  • Inteiros (Integer): representam números inteiros como 1, -5, 42. Podem vir em tamanhos diferentes (short, int, long), cada um com um intervalo específico. O int padrão ocupa 4 bytes (32 bits), o que permite representar valores de -2.147.483.648 a 2.147.483.647.

  • Flutuantes (Float): representam números com parte decimal, como 3.14 ou -0.001. O float ocupa 32 bits e o double ocupa 64 bits — quanto mais bits, maior a precisão.

  • Caracteres (Char): representam um único caractere como 'a', '1' ou '@'. Ocupam 1 byte na codificação ASCII (256 caracteres possíveis). Para suportar outros idiomas e símbolos, linguagens modernas usam UTF-8, onde um caractere pode ocupar de 1 a 4 bytes.

  • Strings: sequências de caracteres como "Olá, Mundo!". Ocupam memória variável, proporcional ao comprimento. Tecnicamente são arrays de chars.

tipotamanhobitsintervalo / exemplo
tipos de valor (stack)
booleanvalor
1 byte
8 bits
true / false
charvalor
1 byte
8 bits
'a', '1', '@' (ASCII)
shortvalor
2 bytes
16 bits
-32.768 → 32.767
intvalor
4 bytes
32 bits
-2.147.483.648 → 2.147.483.647
floatvalor
4 bytes
32 bits
~±3.4 × 10³⁸ (7 dígitos)
longvalor
8 bytes
64 bits
-9.2 × 10¹⁸ → 9.2 × 10¹⁸
doublevalor
8 bytes
64 bits
~±1.8 × 10³⁰⁸ (15 dígitos)
tipos de referência (heap)
stringref
variável
n × 8–32
"olá" = 3–12 bytes (UTF-8)
array / objectref
variável
ilimitado
[1, 2, 3], { key: val }
* Em JavaScript, todos os números são double (64 bits, IEEE 754) — não existe int nativo.
NOTA

Em JavaScript, todos os números são double por padrão (64 bits, IEEE 754). Não existe int nativo — mesmo 42 é armazenado como ponto flutuante. Isso é um detalhe importante para quem vem de Java ou C.

Tipos de Valor vs. Tipos de Referência

Este é o tópico mais importante para qualquer um que queira estudar algoritmos, e é aqui que eu queria chegar desde o começo. Existem dois tipos de dados: os tipos de valor e os tipos de referência.

Tipos de Valor (Primitivos)

São simples e ficam na Stack. Eles armazenam o valor diretamente na variável. Quando você faz b = a, uma cópia independente do valor é criada — modificar b não afeta a.

Exemplos: booleanos, inteiros, flutuantes e caracteres.

let a = 10;
let b = a; // b recebe uma CÓPIA do valor
b = 99;

console.log(a); // 10 — não foi afetado
console.log(b); // 99

Tipos de Referência (Objetos/Arrays)

São complexos e ficam no Heap. A variável não armazena o valor em si — armazena um endereço (referência) que aponta para onde o valor está na memória. Quando você faz b = a, ambas as variáveis passam a apontar para o mesmo lugar.

Exemplos: arrays, objetos e strings (em algumas linguagens).

let a = [1, 2, 3];
let b = a; // b recebe o ENDEREÇO de a, não uma cópia

b.push(4);

console.log(a); // [1, 2, 3, 4] — foi afetado!
console.log(b); // [1, 2, 3, 4]

Esse comportamento é uma das fontes mais comuns de bugs em JavaScript — especialmente quando você passa objetos como argumentos para funções sem perceber que está passando uma referência.

// valor vs referência — visualização interativa

Tipo de Valor (Primitivo)
Armazenado na Stack. A variável contém o valor diretamente.
Estado inicial — apenas a existe
STACK
valor
a
10
0x01
Tipo de Referência (Objeto)
Armazenado no Heap. A variável contém um endereço (ponteiro).
Estado inicial — apenas a existe
STACK
endereço
a
0xA1
0x01
HEAP
0xA1
123
ANÁLOGIA

Pense nos Tipos de Valor como o objeto real que você carrega no bolso. Já os Tipos de Referência são como a chave de um armário: se você der uma cópia da chave para um amigo (b = a), ambos podem abrir o mesmo armário e mudar o que está lá dentro.

NOTA

No JavaScript, os tipos primitivos (boolean, number, string, null, undefined, symbol, bigint) se comportam como tipos de valor. No entanto, strings são imutáveis. Embora pareçam objetos por possuírem métodos, elas se comportam como tipos de valor: quando você altera uma string, o motor do JS cria uma nova na memória em vez de modificar a anterior.

Por que isso importa para algoritmos?

Dois motivos principais: overflow e performance.

Overflow acontece quando você tenta armazenar um número maior do que o tipo suporta. O valor não gera um erro — ele simplesmente "dá a volta" (wrap around):

Underflow é o oposto — tentar representar um número menor do que o tipo permite, resultando em zero ou no menor valor possível.

// Exemplo em C (int de 32 bits)
int x = 2147483647; // valor máximo de um int
x = x + 1;
// x agora é -2147483648 — voltou para o mínimo negativo

// overflow simulator — o que acontece quando os bits acabam

Tipo
Passo
Valor atual
0
MIN -128MAX 127
Representação binária
0 0000 000
bit de sinal   bits de magnitude
Intervalo: -128 127 (8 bits)

Para algoritmos isso é crítico: um índice de array que sofre overflow pode apontar para uma posição inválida de memória, um contador que estoura pode criar um loop infinito, e cálculos financeiros com float podem acumular erros de precisão silenciosos.

Performance: além do risco de erros, tipos de valor são processados muito mais rápido pelo processador (acesso direto na Stack), enquanto tipos de referência exigem que o computador busque o endereço e depois vá até o Heap encontrar o dado real.