Antes de falar como ocorre com os operadores de deslocamento, devemos entender como os números são formados no computador. Sabemos que possuímos 4 tipos de inteiros:
- byte - com 8 bits
- char e short - com 16 bits (sendo que o char não possui o bit de sinal)
- int - com 32 bits
128 64 32 16 8 4 2 1Não precisamos de mais nada, agora devemos fazer uma pergunta para cada bit: 47 é maior ou igual ao seu valor? Se sim marque 1 e subtraia o valor do bit (para o próximo bit use esse resultado ao invés de 47) senão marque 0. Faça isso para todos os números e o resultado será o seguinte:
128 64 32 16 8 4 2 1 0 0 1 0 1 1 1 147 é maior que 32 e sobra 15, 15 é maior que 8 e sobra 7, 7 é maior que 4 e sobra 3, 3 é maior que 2 e sobra 1 e marcamos a coluna do 1 por ser igual. Todos os outros ganham 0. Devemos saber que o primeiro bit é de sinal, se ele fosse marcado o valor seria negativo.
Para converter um binário em decimal, se o número é positivo (ou seja, o bit de sinal é 0) basta somar o valor dos bits ligados (que possuem o valor 1), 32+8+4+2+1 = 47. Se o número fosse negativo faríamos 128 menos a soma dos outros. Curiosamente o número -1 é representado com todos os bits ligados.
Pronto agora que sabemos realizar a conversão, podemos partir para o operador de deslocamento. Apenas devemos ter em mente uma propriedade do C: qualquer operação realizada com tipos inteiros o resultado é automaticamente passado para um tipo int. Ou seja, se deslocarmos os bits de um byte o resultado será um int (e não um byte), o que isso tem a ver? Saímos de 8 bits e entramos em 32 bits, por exemplo: 47 << 2, já sabemos como é a representação binária de 47, então basta andar com os bits 2 casas a esquerda resultando em:
128 64 32 16 8 4 2 1 1 0 1 1 1 1 0 0E parece que agora temos um número negativo, se fosse um tipo byte isso seria verdade, porém a representação do tipo int conta com mais 3 conjuntos desse e o resultado é 188. Observe que preenchemos todos os espaços vazios com 0. Já o deslocamento a direita, por exemplo 47 >> 2 resulta em:
128 64 32 16 8 4 2 1 0 0 0 0 1 0 1 1O que dá o valor 11. Preenchemos com 0 os espaços vazios pois é um valor positivo, se fosse negativo seria preenchido com 1, a diferença para o outro operador com 3 sinais é que nesse sempre é preenchido com 0 (ou seja, um número negativo vira positivo qualquer que seja seu deslocamento).
Tudo bem, entendemos como é realizado o processo, mas para que serve? Deslocamento a esquerda é a multiplicação do número por 2 elevado ao valor deslocado e deslocamento a direita a divisão do número por 2 elevado ao valor deslocado. Isso é realizado para buscar uma melhor performance e simplificar programas que envolvem muitos cálculos matemáticos, vamos comparar a performance em 2 programas escritos em Java e Python usando esses operadores e seus equivalentes matemáticos.
Observação, lembro que Java não existe operador para exponenciação e devemos usar o método Math.pow(n,e), já em Python usamos o operador **.
Em Java
import java.text.SimpleDateFormat; public class Desloca { private SimpleDateFormat sdf = new SimpleDateFormat("hh:mm:ss:SSSS"); public static void main(String [] args) { new Desloca().testar(); } private void testar() { porMultiplicacao(); porDeslocamentoEsq(); porDivisao(); porDeslocamentoDir(); } private void porMultiplicacao() { int i = 3000; double j = 0; System.out.println("Por Multiplicação: " + sdf.format(new java.util.Date())); for (int z = 0; z < 1000000; z++) { j = i * Math.pow(2,7); } System.out.println("Por Multiplicação: " + sdf.format(new java.util.Date())); System.out.println(j); } private void porDeslocamentoEsq() { int i = 3000; double j = 0; System.out.println("Por Desl.Esq: " + sdf.format(new java.util.Date())); for (int z = 0; z < 1000000; z++) { j = i << 7; } System.out.println("Por Desl.Esq: " + sdf.format(new java.util.Date())); System.out.println(j); } private void porDivisao() { int i = 3000; double j = 0; System.out.println("Por Divisão: " + sdf.format(new java.util.Date())); for (int z = 0; z < 1000000; z++) { j = i / Math.pow(2,7); } System.out.println("Por Divisão: " + sdf.format(new java.util.Date())); System.out.println(j); } private void porDeslocamentoDir() { int i = 3000; double j = 0; System.out.println("Por Desl.Dir: " + sdf.format(new java.util.Date())); for (int z = 0; z < 1000000; z++) { j = i >> 7; } System.out.println("Por Desl.Dir: " + sdf.format(new java.util.Date())); System.out.println(j); } }
Em Python
import datetime def porMultiplicacao(): i = 3000 j = 0 print("Multiplicar: ", datetime.datetime.now().time()) for z in range(0, 1000000): j = i * (2 ** 7) print("Multiplicar: ", datetime.datetime.now().time()) print(j); def porDeslocamentoEsq(): i = 3000 j = 0 print("Por Desl.Esq: ", datetime.datetime.now().time()) for z in range(0, 1000000): j = i << 7; print("Por Desl.Esq: ", datetime.datetime.now().time()) print(j); def porDivisao(): i = 3000 j = 0 print("Dividir: ", datetime.datetime.now().time()) for z in range(0, 1000000): j = i / (2 ** 7); print("Dividir: ", datetime.datetime.now().time()) print(j); def porDeslocamentoDir(): i = 3000 j = 0 print("Por Desl.Dir: ", datetime.datetime.now().time()) for z in range(0, 1000000): j = i >> 7; print("Por Desl.Dir: ", datetime.datetime.now().time()) print(j); def testar(): porMultiplicacao() porDeslocamentoEsq() porDivisao() porDeslocamentoDir() if __name__ == '__main__': testar()
Realizamos um milhão de vezes cada cálculo e mostramos o tempo inicial e final. Não estou dizendo que a partir de agora está proibido realizar o operador elevado a dois, quis apenas demonstrar a utilidade e razão da existência deste operador de deslocamento. Usá-lo ou não depende de muitos fatores e ao bom senso dos programadores.
Obrigado e até a próxima
Fernando Anselmo
0 comentários:
Postar um comentário