Ir para o conteúdo principal

Aritmética de Computadores: Soma e Subtração em Binário, Octal e Hexadecimal

Autor
Francisco Bustamante
Um químico trabalhando com Ciência de Dados e Programação em Python.
Tabela de conteúdos
Do Zero ao Float - Este artigo faz parte de uma série de artigos.
Parte 2: Esse Artigo

No artigo anterior, aprendemos como ler e escrever números em diferentes bases, como o binário e o hexadecimal. Agora, vamos dar vida a esses números. Afinal, computadores não apenas armazenam dados; eles os processam.

A boa notícia é que você não precisa reaprender matemática. As regras de soma e subtração que você aprendeu no ensino fundamental para a base decimal (base 10) são universais. O conceito de “vai-um” (carry) ou de “pegar emprestado” funciona exatamente da mesma maneira, quer você esteja contando maçãs (decimal) ou pulsos elétricos (binário).

Neste artigo, vamos revisitar essas operações fundamentais na base 10 para isolar o algoritmo e, em seguida, aplicar exatamente a mesma lógica para realizar cálculos como um computador.

Base decimal
#

A base decimal, também conhecida como base 10, é o sistema numérico mais utilizado no dia a dia. Ele é composto pelos dígitos de 0 a 9 e utiliza um sistema posicional, onde o valor de cada dígito depende de sua posição dentro do número.

Soma na base decimal - regra prática
#

A soma de números decimais segue um processo posicional, onde os dígitos são somados coluna por coluna, começando pela unidade (direita) e avançando para a esquerda.

O “segredo” aqui é generalizar a regra: o “vai-um” ocorre sempre que a soma atinge ou ultrapassa o valor da Base. No caso decimal, se deu 10 ou mais, vai um.

Exemplo: Soma \(67 + 84\) na base 10

$$ \begin{array}{r r r}  & {\scriptstyle 1} & \\  & 6 & 7\\ +& 8 & 4\\ \hline 1& 5 & 1 \end{array} $$

Passo a passo:

  1. Somamos a coluna das unidades: \(7 + 4 = 11\). Como o resultado tem dois dígitos (passou da base), colocamos o 1 na posição das unidades e levamos o outro 1 para a próxima coluna (chamado de “vai-um”).
  2. Somamos a coluna das dezenas: \(6 + 8 = 14\) e somamos o “vai-um”, ficando \(14 + 1 = 15\). Como não há mais colunas, o 15 é colocado como resultado final.

Portanto, o resultado da soma é 151.

O Movimento Vai-Um (Carry)
#

O “vai-um” ocorre quando a soma de dois dígitos resulta em um valor maior que 9, já que 10 é a base do sistema decimal e o “vai-um” ocorre sempre que a soma atinge ou ultrapassa a base, como já vimos. Nesse caso, mantemos o dígito das unidades na coluna correspondente e levamos o dígito das dezenas para a próxima coluna.

Exemplo: Soma \(489 + 276\) na base 10

$$ \begin{array}{r r r}  {\scriptstyle 1} & {\scriptstyle 1} &\\  4 & 8 & 9\\ +2 & 7 & 6\\ \hline  7 & 6 & 5 \end{array} $$
  1. Unidades: \(9 + 6 = 15\), escrevemos o 5 e levamos 1 para a coluna das dezenas.
  2. Dezenas: \(8 + 7 = 15\), somamos o “vai-um” (\(15 + 1 = 16\)), escrevemos 6 e levamos 1 para a próxima coluna.
  3. Centenas: \(4 + 2 = 6\), somamos o “vai-um” (\(6 + 1 = 7\)), resultando em 7.

O resultado final é 765.

Soma na base decimal - algoritmo formal
#

A soma de números decimais segue um processo posicional, como vimos. Para entender melhor como isso funcionará em outras bases, vamos analisar o algoritmo formal: somamos a coluna e verificamos o resultado.

Se o valor for maior ou igual à base (neste caso, 10), não podemos representá-lo com um único dígito. Por isso, subtraímos o valor da base (10 no caso) para encontrar o que fica na coluna atual e, em troca, enviamos 1 para a próxima coluna (o carry ou “vai-um”).

Vamos repetir o exemplo anterior, mas agora detalhando o algoritmo formalmente.

Exemplo: Soma \(67 + 84\) na base 10 (Algoritmo Formal)

$$ \begin{array}{r r r l} & {\scriptstyle 1} & & \\ & 6 & 7 & \\ +& 8 & 4 & \\ \hline & (15) & (11) & \text{somas parciais} \\ -& 10 & 10 & \text{subtraindo a base (10)} \\ \hline 1& 5 & 1 & \end{array} $$

Passo a passo pelo algoritmo:

  1. Unidades: \(7 + 4 = 11\). Como \(11 \ge 10\), fazemos \(11 - 10 = 1\). O 1 fica e geramos um “vai-um”.
  2. Dezenas: \(6 + 8 = 14\). Somamos o “vai-um” (\(15\)). Como \(15 \ge 10\), fazemos \(15 - 10 = 5\). O 5 fica e geramos um “vai-um” para a próxima coluna (centenas).

Subtração na Base Decimal - Regra Prática
#

A subtração também segue um processo posicional. Neste artigo, adotaremos o método conhecido como “carry sub” (ou método da compensação/adições iguais).

Sempre que for preciso “emprestar”, ao invés de diminuir o valor do topo, nós adicionamos 1 na casa imediatamente superior do subtraendo (o número de baixo). Isso mantém a diferença correta, mas deixa o visual da conta mais limpo.

Nota sobre métodos

Talvez você tenha aprendido na escola o método de “pedir emprestado” riscando o número de cima (ex: o 7 vira 6 para emprestar 1 para o 5). Ambos os métodos são matematicamente equivalentes. Usaremos o método da compensação aqui porque ele facilita a organização visual em algoritmos computacionais.

Exemplo: Subtração \(75 - 38\) (carry sub)

$$ \begin{array}{r r r} &7 & {\scriptstyle 1}5\\ -&{\scriptstyle 1+}3 & 8\\ \hline &3 & 7 \end{array} $$

Passo a passo:

  1. Unidades: \(5 - 8\) não é possível. Para contornar isso, somamos 10 ao minuendo na casa das unidades (o \(5\) vira 15) e acrescentamos 1 à casa das dezenas do subtraendo (o \(3\) vira 4). Assim, a diferença permanece inalterada.
  2. Agora subtraímos as unidades: \(15 - 8 = 7\).
  3. Dezenas: após o ajuste (o 3 virou 4), subtraímos \(7 - 4 = 3\).

No final, obtemos \(37\) como resultado.

O Processo de Pegar Emprestado - carry sub
#

No método “carry sub”, sempre que a casa superior não for suficiente para a subtração, adicionamos 10 (valor da base) ao algarismo do minuendo nessa casa e, simultaneamente, acrescentamos 1 à casa imediata do subtraendo.

Exemplo: Subtração \(402 - 189\) (carry sub)

$$ \begin{array}{r r r r} &4 & {\scriptstyle 1}0 & {\scriptstyle 1}2\\ -&{\scriptstyle 1+}1 & {\scriptstyle 1+}8 & 9\\ \hline &2 & 1 & 3 \end{array} $$
  1. Unidades: precisamos fazer \(2 - 9\). Como \(2 < 9\), somamos 10 ao 2 (fica 12) e adicionamos 1 à casa das dezenas do subtraendo (8 vira 9). Agora subtraímos: \(12 - 9 = 3\).
  2. Dezenas: temos agora \(0 - 9\). Novamente não é possível sem “carregar”: somamos 10 ao 0 (vira 10) e acrescentamos 1 à casa das centenas do subtraendo (1 vira 2). Então fazemos \(10 - 9 = 1\).
  3. Centenas: restou \(4 - 2\), que é \(2\).

Dessa forma, obtemos o resultado \(213\).

Subtração na Base Decimal - Algoritmo Formal
#

A subtração também segue um processo posicional. No método de compensação (carry sub), a lógica algorítmica é clara: se o dígito de cima é menor que o de baixo, somamos o valor da base (10 no caso) ao dígito de cima para tornar a subtração possível.

Para manter a equação equilibrada, devemos compensar adicionando 1 à próxima coluna do número de baixo (subtraendo).

Exemplo: Subtração \(75 - 38\) na base 10 (Algoritmo Formal)

$$ \begin{array}{r r r} & 7 & 5^{\scriptscriptstyle+10} \\ -& 3^{\scriptscriptstyle+1} & 8 \\ \hline & 3 & 7 \end{array} $$

Passo a passo pelo algoritmo:

  1. Unidades: \(5 - 8\) não é possível. Somamos a base ao 5 (\(5 + 10 = 15\)). Agora fazemos \(15 - 8 = 7\). Compensamos adicionando \(+1\) ao vizinho de baixo (o 3).
  2. Dezenas: Agora temos \(7\) em cima e \(3 + 1 = 4\) em baixo. Fazemos \(7 - 4 = 3\).

Outras bases
#

Soma em diferentes bases
#

A soma em bases diferentes segue o mesmo princípio da base decimal, o “vai-um” ocorre sempre que o resultado de uma soma ultrapassa o maior valor permitido na base. Agora, o algoritmo formal fica mais evidente: se o valor da soma é maior ou igual à base, subtraímos o valor da base para encontrar o que fica na coluna atual e enviamos 1 para a próxima coluna.

Vamos aos exemplos práticos:

Exemplo: Soma na base 9

A base 9 tem dígitos de 0 a 8. Sempre que a soma é maior ou igual à base (9), o “vai-um” é adicionado à próxima coluna.

$$ \begin{array}{c c c c}     {\scriptscriptstyle 1} & \scriptscriptstyle 1 &  & {} \\     {} & 6 & 7 & {} \\     {} + & 8 & 4 & {} \\ \hline     1 & (15) & (11) & {} \\     {} - & 9 & 9 & {\text{ subtraindo a base}} \\ \hline     1 & 6 & 2 & {} \end{array} $$

Passo a passo:

  1. Unidades: \(7 + 4 = 11\). Escrevemos 2, resultado de \(11 - 9\) (base), e     levamos 1 para a próxima coluna.
  2. Dezenas: \(6 + 8 = 14\). Somamos o “vai-um” e temos \(14 + 1 = 15\). O     resultado é 6 e levamos 1 para a próxima coluna.

Portanto, o resultado da soma \(67 + 84\) na base 9 é \(162_9\).

Exemplo: Soma na base 16

A base 16 tem dígitos de 0 a F (representando 15). Sempre que a soma é maior ou igual à base (16), o “vai-um” é adicionado à próxima coluna. Resultado: \(F1A + E09 = 1D23_{16}\).

$$ \begin{array}{c c c c c}     {\scriptscriptstyle 1} &  & \scriptscriptstyle 1 &  & {} \\     {} & F & 1 & A & {} \\     {} + & E & 0 & 9 & {} \\ \hline     1 & (29) & 2 & (19) & {} \\     {} - & 16 &  & 16 & {\text{ subtraindo a base}} \\ \hline     1 & D & 2 & 3 & {} \end{array} $$

Resultado: \(\mathtt{F1A} + \mathtt{E09} = \mathtt{1D23_{16}}\).

Antes de ver os exemplos em base 2, vale a pena ter em mente a “tabuada” básica da soma binária, que é onde a maioria das confusões acontece:

  • \(0 + 0 = 0\)
  • \(0 + 1 = 1\)
  • \(1 + 0 = 1\)
  • \(1 + 1 = 10_2\) (Fica 0 e vai 1)
  • \(1 + 1 + 1 = 11_2\) (Fica 1 e vai 1)
Exemplo: Soma na base 2

A base 2 tem dígitos 0 e 1. Sempre que a soma é maior ou igual à base (2), o “vai-um” é adicionado à próxima coluna.

$$ \begin{array}{c c c c c c c}     \scriptscriptstyle 1 & \scriptscriptstyle 1 & \scriptscriptstyle 1 & \scriptscriptstyle 1 & \scriptscriptstyle 1 & & {} \\     {} & 1 & 0 & 1 & 1 & 1 & {} \\     {} + & 1 & 1 & 0 & 1 & 1 & {} \\ \hline        & (3) & (2) & (2) & (3) & (2) & {} \\     {} - & 2 & 2 & 2 & 2 & 2 & {\text{ subtraindo a base}} \\ \hline     1 & 1 & 0 & 0 & 1 & 0 & {} \end{array} $$

Resultado: \(10111_2 + 11011_2 = 110010_2\).

Na base 2, vimos que \(1_2 + 1_2 = 10_2\) e \(1_2 + 1_2 + 1_2 = 11_2\). Assim, pode ser mais fácil somar números binários se lembrarmos dessas contas de cabeça.

Exemplo: Soma na base 2 usando conhecimento dos valores de 2 e 3 na base

$$ \begin{array}{c c c c c c c}     \scriptscriptstyle 1 & \scriptscriptstyle 1 & \scriptscriptstyle 1 & & &  & {} \\     {} & 1 & 0 & 1 & 0 & 0 & {} \\     {} + & 1 & 1 & 1 & 0 & 1 & {} \\ \hline     1 & 1 & 0 & 0 & 0 & 1 & {} \end{array} $$

Subtração em diferentes bases
#

A subtração em bases diferentes segue o mesmo princípio que vimos na base decimal (carry sub/compensação): se o dígito de cima for menor que o de baixo, somamos a base ao de cima e compensamos somando 1 ao vizinho de baixo.

Novamente, o algoritmo formal fica mais evidente: se o valor da subtração é negativo, somamos o valor da base ao dígito de cima para tornar a subtração possível e adicionamos 1 à próxima coluna do número de baixo (subtraendo) para manter a equação equilibrada.

Exemplo: Subtração na base 8

Na base 8, os dígitos vão de 0 a 7. Sempre que a subtração resulta em um número negativo, emprestamos 1 da próxima coluna.

$$ \begin{array}{c c c} {} & 5 & 2^{\scriptscriptstyle+8} \\ {} - & 1^{\scriptscriptstyle+1} & 7 \\ \hline {} & 3 & 3 \end{array} $$
Exemplo: Subtração na base 16

Na base 16, os dígitos vão de 0 a F. Sempre que a subtração resulta em um número negativo, emprestamos 1 da próxima coluna.

$$ \begin{array}{c c c} {} & 5 & 2^{\scriptscriptstyle+16} \\ {} - & 1^{\scriptscriptstyle+1} & 7 \\ \hline {} & 3 & B \end{array} $$

Na base 2, podemos usar o conhecimento já adquirido da “tabuada” para facilitar a subtração.

Exemplo: Subtração na base 2

$$ \begin{array}{c c c c c c c} {} & 1 & 1 & {\scriptscriptstyle1}0 & {\scriptscriptstyle1}0 & 1 & {\scriptscriptstyle1}0 \\ {} - & 1 & 0^{\scriptscriptstyle+1} & 1^{\scriptscriptstyle+1} & 1 & 0^{\scriptscriptstyle+1} & 1 \\ \hline & 0 & 0 & 0 & 1 & 0 & 1 \end{array} $$

Algoritmos Formais e Implementação
#

Para um cientista da computação, entender a aritmética não é apenas saber fazer a conta no papel, mas entender o algoritmo que pode ser programado. Abaixo, apresentamos a lógica generalizada para qualquer Base \(B\), primeiro em pseudocódigo e depois em Python.

1. Algoritmo de Soma (Generalizado)
#

A lógica central depende de duas operações inteiras: o resto da divisão (%) determina o dígito que fica, e a divisão inteira (//) determina o “vai-um”.

Para começar, vamos ver em pseudocógido, que é uma forma de escrever algoritmos de maneira independente de linguagem de programação. Talvez facilite a compreensão antes de vermos o código Python.

Pseudocódigo:

FUNÇÃO Somar(Numero_A, Numero_B, BASE):
    vai_um = 0
    resultado = ""

    PARA cada posição i (da direita para a esquerda):
        soma_parcial = digito_a + digito_b + vai_um

        # O dígito que fica é o resto da divisão pela base
        digito_que_fica = soma_parcial MOD BASE

        # O "vai-um" é a divisão inteira pela base
        vai_um = soma_parcial DIV BASE

        ADICIONAR digito_que_fica À ESQUERDA de resultado

    SE vai_um > 0: ADICIONAR vai_um À ESQUERDA de resultado
    RETORNAR resultado

Implementação em Python:

Para os códigos Python, usaremos uma string auxiliar para mapear dígitos maiores que 9: DIGITOS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"

DIGITOS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"

def somar_bases(n1: str, n2: str, base: int) -> str:
    # Inverte as strings para facilitar a iteração da direita para esquerda
    n1, n2 = n1[::-1].upper(), n2[::-1].upper()
    
    # Iguala o tamanho das strings com zeros à direita (que são à esquerda no número real)
    tam_max = max(len(n1), len(n2))
    n1 = n1.ljust(tam_max, '0')
    n2 = n2.ljust(tam_max, '0')
    
    vai_um = 0
    resultado = []
    
    for i in range(tam_max):
        # Converte caractere para valor numérico (Ex: 'A' -> 10)
        v1 = DIGITOS.index(n1[i])
        v2 = DIGITOS.index(n2[i])
        
        soma_parcial = v1 + v2 + vai_um
        
        # Lógica da Base
        digito_fica = soma_parcial % base
        vai_um = soma_parcial // base
        
        resultado.append(DIGITOS[digito_fica])
        
    if vai_um > 0:
        resultado.append(DIGITOS[vai_um])
        
    # Inverte de volta para retornar a string correta
    return "".join(resultado[::-1])

# --- Exemplos de Execução (Baseados nos exemplos do artigo) ---

# Base 10: 67 + 84
print(f"Base 10: {somar_bases('67', '84', 10)}")
# Resultado: 151

# Base 9: 67 + 84
print(f"Base 9:  {somar_bases('67', '84', 9)}") 
# Resultado: 162 (Note que 7+4=11, que é 12 na base 9)

# Base 16: F1A + E09
print(f"Base 16: {somar_bases('F1A', 'E09', 16)}") 
# Resultado: 1D23

# Base 2: 10111 + 11011
print(f"Base 2:  {somar_bases('10111', '11011', 2)}") 
# Resultado: 110010

2. Algoritmo de Subtração (Método Carry Sub)
#

Neste método, quando o dígito de cima é menor, somamos a BASE a ele e marcamos uma compensacao (+1) para ser adicionada ao dígito de baixo na próxima coluna.

Pseudocódigo:

FUNÇÃO Subtrair(Minuendo, Subtraendo, BASE):
    compensacao = 0 
    resultado = ""

    PARA cada posição i (da direita para a esquerda):
        digito_baixo = Subtraendo[i] + compensacao
        digito_cima = Minuendo[i]

        SE digito_cima < digito_baixo:
            # "Pega emprestado" da base (soma a base ao topo)
            digito_cima = digito_cima + BASE
            compensacao = 1
        SENÃO:
            compensacao = 0

        diferenca = digito_cima - digito_baixo
        ADICIONAR diferenca À ESQUERDA de resultado

    RETORNAR resultado

Implementação em Python:

def subtrair_bases(n1: str, n2: str, base: int) -> str:
    # Assumindo que n1 >= n2 para evitar números negativos
    n1, n2 = n1[::-1].upper(), n2[::-1].upper()
    tam_max = max(len(n1), len(n2))
    n1 = n1.ljust(tam_max, '0')
    n2 = n2.ljust(tam_max, '0')
    
    compensacao = 0
    resultado = []
    
    for i in range(tam_max):
        v_cima = DIGITOS.index(n1[i])
        v_baixo = DIGITOS.index(n2[i]) + compensacao
        
        if v_cima < v_baixo:
            # Aplica a lógica do Carry Sub (soma a base em cima)
            v_cima += base
            compensacao = 1
        else:
            compensacao = 0
            
        diferenca = v_cima - v_baixo
        resultado.append(DIGITOS[diferenca])
        
    # Remove zeros à esquerda resultantes (ex: 037 -> 37)
    return "".join(resultado[::-1]).lstrip('0') or "0"

# --- Exemplos de Execução ---

# Base 10: 75 - 38
print(f"Base 10: {subtrair_bases('75', '38', 10)}")
# Resultado: 37

# Base 8: 52 - 17 (Exemplo do artigo: pegamos emprestado 8)
print(f"Base 8:  {subtrair_bases('52', '17', 8)}")
# Resultado: 33 (Pois 2 virou 10 (2+8), 10-7=3. O 1 virou 2, 5-2=3)

# Base 16: 52 - 17
print(f"Base 16: {subtrair_bases('52', '17', 16)}")
# Resultado: 3B (Pois 2 virou 18 (2+16), 18-7=11(B). O 1 virou 2, 5-2=3)

# Base 2: 1101010 - 101101 (Exemplo adaptado)
print(f"Base 2:  {subtrair_bases('1101010', '101101', 2)}")
# Resultado: 111101

Conclusão e Próximos Passos
#

Como vimos, a aritmética posicional é consistente: não importa se temos 2 símbolos (binário) ou 16 (hexadecimal), o algoritmo é o mesmo. A única coisa que muda é o valor que dispara o “vai-um” ou o “empréstimo”.

Dominar essas operações no papel é excelente para entender a lógica, mas levanta uma questão importante: será que o hardware do computador realiza subtrações pedindo emprestado bits vizinhos?

Na prática, construir circuitos que subtraem é caro e complexo. Por isso, a computação moderna usa um truque inteligente para transformar subtrações em somas, utilizando o conceito de Complemento de Dois e números com sinal. É exatamente sobre essa representação de números negativos que falaremos no próximo artigo.

Até lá!

Do Zero ao Float - Este artigo faz parte de uma série de artigos.
Parte 2: Esse Artigo

Relacionados

Séries infinitas - Cálculo com Python e SymPy

Um bom tempo do estudo de cálculo é destinado às chamadas séries infinitas. Mas, afinal, o que são séries infinitas? E qual sua relação com o paradoxo de Aquiles e a tartaruga, um dos chamados paradoxos de Zenão de Eleia, filósofo grego? Qual a relação com quântica e o princípio da incerteza? E onde entra Python nisto tudo? É o que descobriremos neste artigo.