arrow-left

Todas as páginas
gitbookFornecido por GitBook
1 de 4

Loading...

Loading...

Loading...

Loading...

Hacks com Deslocamento

Você pode usar os operadores de deslocamento para diversos hacks (soluções engenhosas) úteis. Por exemplo, dada uma data especificada pelo dia do mês d, mês m e ano y, você pode armazenar toda a data em um único inteiro date:

unsigned int d = 12;        /* 12 em binário é 0b1100.  */
unsigned int m = 6;         /* 6 em binário é 0b110.  */
unsigned int y = 1983;      /* 1983 em binário é 0b11110111111.  */
unsigned int date = (((y << 4) + m) << 5) + d;
/* Adiciona 0b11110111111000000000
   e 0b11000000 e 0b1100.
   A soma é 0b11110111111011001100.  */

Para extrair o dia, mês e ano de date, use uma combinação de deslocamento e resto:

/* 32 em binário é 0b100000.  */
/* O resto da divisão por 32 dá os 5 bits menos significativos, 0b1100.  */
d = date % 32;
/* Deslocar 5 bits para a direita descarta o dia, restando 0b111101111110110.
   O resto da divisão por 16 dá os 4 bits menos significativos restantes, 0b110.  */
m = (date >> 5) % 16;
/* Deslocar 9 bits para a direita descarta dia e mês,
   restando 0b111101111110.  */
y = date >> 9;

-1 << LOWBITS é uma maneira inteligente de criar um inteiro cujos LOWBITS bits menos significativos são todos 0 e o restante são todos 1. -(1 << LOWBITS) é equivalente a isso, devido à associatividade da multiplicação, já que negar um valor é equivalente a multiplicá-lo por -1.

Deslocar Gera Novos Bits

Uma operação de deslocamento move os bits para uma extremidade do número e precisa gerar novos bits na outra extremidade.

Deslocar para a esquerda um bit deve gerar um novo bit menos significativo. Ele sempre insere um zero ali. Isso é equivalente a multiplicar pela potência de 2 correspondente. Por exemplo,

5 << 3     é equivalente a   5 * 2*2*2
-10 << 4   é equivalente a   -10 * 2*2*2*2

O significado de deslocar para a direita depende se o tipo de dado é com sinal ou sem sinal (veja Tipos Com ou Sem Sinal). Para um tipo de dado com sinal, ele realiza um “deslocamento aritmético,” que mantém o sinal do número inalterado ao duplicar o bit de sinal. Para um tipo de dado sem sinal, ele realiza um “deslocamento lógico,” que sempre insere zeros no bit mais significativo.

Em ambos os casos, deslocar para a direita um bit é uma divisão por dois, arredondando em direção ao infinito negativo. Por exemplo,

(unsigned) 19 >> 2 ⇒ 4
(unsigned) 20 >> 2 ⇒ 5
(unsigned) 21 >> 2 ⇒ 5

Para um operando esquerdo negativo a, a >> 1 não é equivalente a a / 2. Ambos dividem por 2, mas ‘/’ arredonda em direção a zero.

A contagem de deslocamento deve ser zero ou maior. Deslocar por um número negativo de bits produz resultados dependentes da máquina.

Operações de Deslocamento

Deslocar um inteiro significa mover os valores dos bits para a esquerda ou para a direita dentro dos bits do tipo de dado. O deslocamento é definido apenas para inteiros. Aqui está a forma de escrever:

/* Deslocamento à esquerda.  */
5 << 2 ⇒ 20

/* Deslocamento à direita.  */
5 >> 2 ⇒ 1

O operando à esquerda é o valor a ser deslocado, e o operando à direita indica quantos bits deslocá-lo (a contagem de deslocamento). O operando à esquerda é promovido (veja Promoções de Operando), então o deslocamento nunca opera em um tipo de inteiro estreito; é sempre int ou mais amplo. O resultado da operação de deslocamento tem o mesmo tipo que o operando esquerdo promovido.

Advertências em Operações de Deslocamento

Aviso: Se a contagem de deslocamento for maior ou igual à largura em bits do primeiro operando promovido, os resultados dependem da máquina. Logicamente falando, o valor "correto" seria -1 (para deslocamento à direita de um número negativo) ou 0 (em todos os outros casos), mas o resultado real é o que a instrução de deslocamento da máquina faz nesse caso. Portanto, a menos que você possa provar que o segundo operando não é grande demais, escreva código para verificá-lo em tempo de execução.

Aviso: Nunca confie na relação entre os operadores de deslocamento e outros operadores binários aritméticos em termos de precedência. Programadores não lembram dessas precedências e não entenderão o código. Sempre use parênteses para especificar explicitamente o aninhamento, assim:

a + (b << 5)   /* Desloca primeiro, depois soma.  */
(a + b) << 5   /* Soma primeiro, depois desloca.  */

Nota: De acordo com o padrão C, o deslocamento de valores com sinal não é garantido para funcionar corretamente quando o valor deslocado é negativo ou se torna negativo durante a operação de deslocamento à esquerda. No entanto, apenas as pessoas mais rigorosas teriam motivo para se preocupar com isso; apenas computadores com instruções de deslocamento estranhas poderiam, plausivelmente, fazer isso de forma incorreta. No GNU C, a operação sempre funciona como esperado.