Os dígitos binários acabam correspondendo diretamente aos transistores da sua CPU ou da sua RAM, e ao fato de cada um estar "ligado" ou "desligado".
A manipulação de baixo nível, chamada informalmente de "bit-twiddling", é especialmente importante em linguagens de sistema.
Linguagens de alto nível como Julia geralmente abstraem a maior parte desse detalhe. Ainda assim, uma gama completa de operações em nível de bits está disponível na linguagem base.
Nota: Para ver uma saída binária legível por humanos no REPL, quase todos os exemplos abaixo precisam estar envolvidos por uma função bitstring(). Isso atrapalha visualmente, então a maioria das ocorrências dessa função foi removida.
Tipos inteiros, com ou sem sinal, podem ser representados como uma string de 1s e 0s.
julia> bitstring(UInt8(5))
"00000101"
Os deslocamentos de bits simplesmente movem tudo para a esquerda ou para a direita por um número especificado de posições. Com tipos UInt, alguns bits saem por uma extremidade, e a outra extremidade é preenchida com zeros:
julia> ux::UInt8 = 5
5
julia> bitstring(ux)
"00000101"
julia> ux << 2 # left by 2
"00010100"
julia> ux >> 1 # right by 1
"00000010"
Cada deslocamento para a esquerda dobra o valor e cada deslocamento para a direita o divide pela metade (sujeito a truncamento). Isso fica mais óbvio na representação decimal:
julia> 3 << 2
12
julia> 24 >> 3
3
Esse tipo de deslocamento de bits é muito mais rápido que aritmética "de verdade", o que torna a técnica muito popular na programação de baixo nível.
Com inteiros com sinal, precisamos tomar um pouco mais de cuidado.
Os deslocamentos para a esquerda são relativamente simples:
julia> sx = Int8(5)
5
julia> sx # positive integer
"00000101"
julia> sx << 2
"00010100"
julia> -sx # negative integer
"11111011"
julia> -sx << 2
"11101100"
Deslocar inteiros positivos com sinal para a esquerda é, portanto, o mesmo que com inteiros sem sinal.
Valores negativos são armazenados na forma de complemento de dois, o que significa que o bit mais à esquerda é 1. Sem problema para um deslocamento à esquerda, mas na hora de deslocar à direita, como preenchemos os bits mais à esquerda?
julia> sx >> 2 # simple for positive values!
"00000001"
julia> -sx # negative integer
"11111011"
julia> -sx >> 2 # pad with repeated sign bit
"11111110"
julia> -sx >>> 2 # pad with 0
"00111110"
O operador >> realiza deslocamento aritmético, preservando o bit de sinal.
O operador >>> realiza deslocamento lógico, preenchendo com zeros como se o número não tivesse sinal.
Vimos em um Conceito anterior que os operadores && (and), || (or) e ! (not) são usados com valores Boolean.
Existem operadores equivalentes, & (and bit a bit), | (or bit a bit) e ~ (um til, not bit a bit), para comparar os bits de dois inteiros.
julia> 0b1011 & 0b0010 # bit is 1 in both numbers
"00000010"
julia> 0b1011 | 0b0010 # bit is 1 in at least one number
"00001011"
julia> ~0b1011 # flip all bits
"11110100"
julia> xor(0b1011, 0b0010) # bit is 1 in exactly one number, not both
"00001001"
Aqui, xor() é o ou exclusivo, usado como função.
Seu amigo acabou de te enviar uma mensagem com um segredo importante. Como ele não queria facilitar a leitura por outras pessoas, a mensagem foi criptografada por meio de uma série de manipulações de bits. Você vai precisar escrever as funções que ajudam a descriptografar a mensagem.
O primeiro passo para descriptografar a mensagem é desfazer o deslocamento do processo de criptografia, deslocando os bits de volta para a direita. Haverá outros passos no processo de descriptografia que assumem que 0s são inseridos pelo lado esquerdo.
Implemente a função shift_back(), que recebe um valor e o número de posições a deslocar, e realiza o deslocamento.
shift_back(0b1001, 2) # => 0b0010
Agora, há alguns bits que precisam ser definidos como 1.
Implemente a função set_bits(), que recebe um valor e uma máscara e retorna o resultado de definir como 1 os bits do valor.
Um bit de value deve ser definido como 1 onde o bit em mask também for 1.
Todos os outros bits devem permanecer inalterados.
set_bits(0b0110, 0b0101) # => 0b0111
Alguns bits são invertidos durante a criptografia. Eles precisarão ser invertidos de volta para descriptografar a mensagem.
Implemente a função flip_bits(), que recebe um valor e a máscara.
A máscara indica quais bits do valor devem ser invertidos.
Se o bit for 1 em mask, o bit é invertido em value.
Todos os outros bits permanecem inalterados.
flip_bits(0b1100, 0b0101) # => 0b1001
Por último, também há certos bits que sempre descriptografam para 0.
Implemente a função clear_bits(), que recebe um valor e uma máscara.
Os bits de value devem ser definidos como 0 onde o bit na máscara for 1.
Todos os outros bits devem permanecer inalterados.
clear_bits(0b0110, 0b0101) # => 0b0010
Crie sua conta no Exercism para aprender e dominar Julia com 35 conceitos128 exercícios e mentoria humana de verdade, tudo de graça.