Atualizado: 07/12/2024

Operações Bit a Bit - JavaScript

Representação Binária dos Números

No nível do computador, todos os dados são representados como um conjunto de bits. Cada bit pode ter dois valores: 1 (há sinal) e 0 (não há sinal). E todos os dados são de fato um conjunto de zeros e uns. 8 bits compõem 1 byte. Esse sistema é chamado de sistema binário.

Por exemplo, o número 13 no sistema binário seria 1101₂. Veja como chegamos a isso:

// conversão do número decimal 13 para o sistema binário
13 / 2 = 6 // resto 1 (13 - 6 * 2 = 1)
6 / 2 = 3 // resto 0 (6 - 3 * 2 = 0)
3 / 2 = 1 // resto 1 (3 - 1 * 2 = 1)
1 / 2 = 0 // resto 1 (1 - 0 * 2 = 1)

O algoritmo geral consiste em dividir sequencialmente o número e os resultados da divisão por 2 e obter os restos, até chegar a 0. Em seguida, alinhamos os restos em linha na ordem inversa para assim formar a representação binária do número. Especificamente neste caso, passo a passo:

  • Dividimos o número 13 por 2. O resultado da divisão é 6, o resto da divisão é 1 (pois 13 - 6 * 2 = 1)

  • Em seguida, dividimos o resultado da divisão anterior - o número 6 por 2. O resultado da divisão é 3, o resto da divisão é 0

  • Dividimos o resultado da divisão anterior - o número 3 por 2. O resultado da divisão é 1, o resto da divisão é 1

  • Dividimos o resultado da divisão anterior - o número 1 por 2. O resultado da divisão é 0, o resto da divisão é 1

  • O último resultado da divisão é 0, então concluímos o processo e alinhamos os restos das divisões, começando pelo último - 1101

Para converter de volta do sistema binário para o decimal, multiplicamos o valor de cada bit (1 ou 0) pelo número 2 elevado ao expoente igual ao número do bit (a numeração dos bits começa do zero):

// conversão do número binário 1101 para o sistema decimal
1(3º bit) 1(2º bit) 0(1º bit) 1(0º bit)
1 * 2³ + 1 * 2² + 0 * 2¹ + 1 * 2⁰
1 * 8 + 1 * 4 + 0 * 2 + 1 * 1
8 + 4 + 0 + 1

No JavaScript, para definir números no formato binário, um prefixo 0b é usado antes do número:

const num = 0b1101; // 13 no sistema decimal
console.log(num); // 13

Representação de Números Negativos

Para representar números com sinal em JavaScript, usa-se o código complementar a dois (two's complement), no qual o dígito mais significativo é o sinal. Se o seu valor é 0, então o número é positivo, e sua representação binária é a mesma que a de um número sem sinal. Por exemplo, 0000 0001 em decimal é 1.

Se o dígito mais significativo é 1, então estamos lidando com um número negativo. Por exemplo, 1111 1111 em decimal representa -1. Da mesma forma, 1111 0011 representa -13.

Para converter um número positivo em negativo, é necessário inverter seus bits e adicionar um:

Complemento a dois

Por exemplo, vamos pegar o número -3. Para isso, primeiro pegamos a representação binária do número 3:

3₁₀ = 0000 0011₂ 

Invertemos os bits:

~0000 0011 = 1111 1100

E adicionamos 1:

1111 1100 + 1 = 1111 1101

Assim, o número 1111 1101 é a representação binária do número -3.

Vamos considerar como será a soma de um número com sinal e sem sinal. Por exemplo, somemos 12 e -8:

12₁₀ = 00001100₂
+
-8₁₀ = 11111000₂ (8 = 00001000, após inversão = 11110111, após +1 = 11111000)
=
4₁₀ = 00000100₂

No sistema binário o resultado é 00000100₂ ou 4₁₀ no sistema decimal.

Podemos ver isso na prática:

let num = 0b1100; // 12 em decimal
num = ~num; // inversão dos bits
num = num + 1;
console.log(num); // -12

Operações de Deslocamento (shift)

Cada número inteiro na memória é representado por uma quantidade específica de bits. As operações de deslocamento permitem mover a representação binária do número vários bits para a direita ou para a esquerda. Essas operações são aplicadas apenas a operandos inteiros. Existem duas operações:

  • << (deslocamento à esquerda)

    Desloca a representação binária do número, representado pelo primeiro operando, para a esquerda por um número específico de bits, que é determinado pelo segundo operando.

    const res = 2 << 2;    // 10 deslocado dois bits para a esquerda = 1000 (8 no sistema decimal)
    console.log(res);      // 8

    O número 2 em representação binária é 0010₂. Se deslocarmos o número 0010 dois bits para a esquerda, resultará em 1000, que no sistema decimal é igual a 8.

  • >> (deslocamento aritmético à direita)

    Desloca a representação binária do número para a direita por um número específico de bits.

    const res = 16 >> 3;  // 10000 deslocado três bits para a direita = 10 (2 no sistema decimal)
    console.log(res);     // 2

    O número 16 em representação binária é 10000₂. Se deslocarmos o número 10000 três bits para a direita (os três últimos bits são descartados), resultará em 00010, que no sistema decimal representa o número 2.

    É importante notar que isso é chamado de deslocamento aritmético, no qual os bits deslocados na parte esquerda são preenchidos com o bit de sinal - 0 para números positivos e 1 para negativos. Assim, ao deslocar números negativos, não há risco de eles se tornarem positivos. Por exemplo:

    const res = -16 >> 3;  // 11111110000 deslocado três bits para a direita = 1111111111111110
    console.log(res);      // -2
    Assim, ao deslocar -16 três bits para a direita, obtemos -2, o que é bastante natural.
  • >>> (deslocamento lógico à direita)

    Desloca a representação binária do número para a direita por um número específico de bits, mas ao contrário da operação >>, preenche os bits deslocados na parte esquerda com zeros, resultando em um deslocamento sem sinal. Assim, mesmo que desloquemos um número negativo, o resultado será sempre positivo:

    const res = -16 >>> 3;
    console.log(res);      // 536870910
    

    Pode-se notar que um deslocamento de um bit para a esquerda é efetivamente equivalente à multiplicação por 2, enquanto que um deslocamento para a direita por um bit é equivalente à divisão por dois. Podemos generalizar: deslocar à esquerda por n é equivalente a multiplicar o número por 2^n, e deslocar à direita por n bits é equivalente a dividir por 2^n, o que pode ser usado em lugar de multiplicar/dividir por potências de dois:

    const res1 = 8 << 2;  // equivalente a 8 * 4
    console.log(res1);      // 32
    
    const res2 = 64 >> 4;  // equivalente a 64 / 16
    console.log(res2);      // 4

Operações Bit-a-Bit (Bitwise Operations)

As operações bit a bit também são realizadas apenas sobre os bits correspondentes dos números:

  • &: conjunção bit a bit (operação E ou multiplicação bit a bit). Retorna 1 se ambos os bits correspondentes de ambos os números forem 1

  • |:disjunção bit a bit (operação OU ou adição bit a bit). Retorna 1 se pelo menos um dos bits correspondentes de ambos os números for 1

  • ^:OU exclusivo bit a bit. Retorna 1 se apenas um dos bits correspondentes de ambos os números for 1

  • ~:negação bit a bit ou inversão. Inverte todos os bits do operando. Se um bit é 1, ele se torna 0, e se é 0, ele se torna 1.

Aplicação das operações:

const a = 5 | 2;            // 101 | 010 = 111  - 7
const b = 6 & 2;            // 110 & 010 = 010  - 2
const c = 5 ^ 2;            // 101 ^ 010 = 111 - 7
const d = ~9;               // -10

Por exemplo, a expressão 5 | 2 é igual a 7. O número 5 em notação binária é 101, e o número 2 é 10 ou 010. Somamos os bits correspondentes de ambos os números. Na soma, se pelo menos um bit é 1, então o resultado dos bits correspondentes é 1. Portanto, obtemos:

1	0	1
0	1	0
1	1	1

O resultado é 111, que em notação decimal representa o número 7.

Vejamos outro exemplo com a expressão 6 & 2. O número 6 em notação binária é 110, e o número 2 é 10 ou 010. Multiplicamos os bits correspondentes de ambos os números. O produto dos bits correspondentes é 1 se ambos os bits são 1. Caso contrário, o produto é 0. Portanto, obtemos:

1	1	0
0	1	0
0	1	0

O resultado é 010, que em notação decimal é igual a 2.

Política de Privacidade

Copyright © www.programicio.com Todos os direitos reservados

É proibida a reprodução do conteúdo desta página sem autorização prévia do autor.

Contato: programicio@gmail.com