Operadores Java Bitwise
1. Visão geral
Operators são usados na linguagem Java para operar em dados e variáveis.
Neste tutorial, vamos explorar Operadores Bitwise e como eles funcionam em Java.
2. Operadores bit a bit
Bitwise operators work on binary digits or bits of input values. Podemos aplicá-los aos tipos inteiros -long, int, short, char,ebyte.
Antes de explorar os diferentes operadores bit a bit, vamos primeiro entender como eles funcionam.
Operadores bit a bit trabalham em um equivalente binário de números decimais e executam operações neles bit a bit de acordo com o operador fornecido:
-
Primeiro, os operandos são convertidos em sua representação binária
-
Em seguida, o operador é aplicado a cada número binário e o resultado é calculado
-
Finalmente, o resultado é convertido novamente em sua representação decimal
Vamos entender com um exemplo; vamos pegar doisintegers:
int value1 = 6;
int value2 = 5;
A seguir, vamos aplicar um operador OR bit a bit a estes números:
int result = 6 | 5;
Para executar esta operação, primeiro, a representação binária desses números será calculada:
Binary number of value1 = 0110
Binary number of value2 = 0101
Em seguida, a operação será aplicada a cada bit. O resultado retorna um novo número binário:
0110
0101
-----
0111
Finalmente, o resultado0111 will ser convertido de volta para decimal que é igual a7:
result : 7
Os operadores bit a bit são ainda classificados como operadores lógicos e de mudança bit a bit. Vamos agora examinar cada tipo.
3. Operadores lógicos bit a bit
Os operadores lógicos bit a bit são AND (&), OR (|), XOR (^) e NOT (~).
3.1. OR bit a bit (|)
O operador OR compara cada dígito binário de dois inteiros e retorna 1 se algum deles for 1.
Isso é semelhante ao || operador lógico usado com booleanos. Quando dois booleanos são comparados, o resultado étrue se algum deles fortrue. Da mesma forma, a saída é 1 quando qualquer um deles for 1.
Vimos um exemplo desse operador na seção anterior:
@Test
public void givenTwoIntegers_whenOrOperator_thenNewDecimalNumber() {
int value1 = 6;
int value2 = 5;
int result = value1 | value2;
assertEquals(7, result);
}
Vejamos a representação binária desta operação:
0110
0101
-----
0111
Aqui, podemos ver que usar OR, 0 e 0 resultará em 0, enquanto qualquer combinação com pelo menos 1 resultará em 1.
3.2. AND bit a bit (&)
O operador AND compara cada dígito binário de dois inteiros e retorna 1 se ambos forem 1, caso contrário, ele retorna 0.
Isso é semelhante ao operador && com valoresboolean. Quando os valores de doisbooleans sãotrue, o resultado de uma operação && étrue.
Vamos usar o mesmo exemplo acima, exceto que agora usamos o operador & em vez do | operador:
@Test
public void givenTwoIntegers_whenAndOperator_thenNewDecimalNumber() {
int value1 = 6;
int value2 = 5;
int result = value1 & value2;
assertEquals(4, result);
}
Vejamos também a representação binária desta operação:
0110
0101
-----
0100
0100 é4 em decimal, portanto, o resultado é:
result : 4
3.3. XOR bit a bit (^)
The XOR operator compares each binary digit of two integers and gives back 1 if both the compared bits are different. Isso significa que se os bits de ambos os inteiros forem 1 ou 0, o resultado será 0; caso contrário, o resultado será 1:
@Test
public void givenTwoIntegers_whenXorOperator_thenNewDecimalNumber() {
int value1 = 6;
int value2 = 5;
int result = value1 ^ value2;
assertEquals(3, result);
}
E a representação binária:
0110
0101
-----
0011
0011 é 3 em decimal, portanto, o resultado é:
result : 3
3.4. COMPLEMENTO bit a bit (~)
Operador Bitwise Not ou Complement significa simplesmente a negação de cada bit do valor de entrada. Leva apenas um número inteiro e é equivalente ao! operador.
Este operador altera cada dígito binário do número inteiro, o que significa que todos os 0 se tornam 1 e todos os 1 se tornam 0. O ! O operador funciona de maneira semelhante para os valores deboolean: ele inverte os valores deboolean detrue parafalse e vice-versa.
Agora vamos entender com um exemplo como encontrar o complemento de um número decimal.
Vamos fazer o complemento de valor1 = 6:
@Test
public void givenOneInteger_whenNotOperator_thenNewDecimalNumber() {
int value1 = 6;
int result = ~value1;
assertEquals(-7, result);
}
O valor em binário é:
value1 = 0000 0110
Ao aplicar o operador complemento, o resultado será:
0000 0110 -> 1111 1001
Este é o complemento do número decimal 6. E como o primeiro bit (mais à esquerda) é 1 em binário, significa que o sinal é negativo para o número armazenado.
Agora, como os números são armazenados como complemento de 2, primeiro precisamos encontrar o complemento de 2 e depois converter o número binário resultante em um número decimal:
1111 1001 -> 0000 0110 + 1 -> 0000 0111
Finalmente, 0000 0111 é 7 em decimal. Como o bit do sinal foi 1, como mencionado acima, a resposta resultante é:
result : -7
3.5. Tabela de operadores bit a bit
Vamos resumir o resultado dos operadores que vimos até agora em uma tabela de comparação:
A B A|B A&B A^B ~A
0 0 0 0 0 1
1 0 1 0 1 0
0 1 1 0 1 1
1 1 1 1 0 0
4. Operadores de deslocamento bit a bit
Os operadores de deslocamento binário deslocam todos os bits do valor de entrada para a esquerda ou direita com base no operador de deslocamento.
Vamos ver a sintaxe para esses operadores:
value
O lado esquerdo da expressão é o número inteiro que é deslocado e o lado direito da expressão indica o número de vezes que ele deve ser deslocado.
Os operadores de turno bit a bit são classificados ainda como operadores de turno bit a esquerda e bit a direita.
4.1. Turno à esquerda assinado [<<]
O operador de deslocamento para a esquerda desloca os bits para a esquerda pelo número de vezes especificado pelo lado direito do operando. Após o deslocamento para a esquerda, o espaço vazio à direita é preenchido com 0.
Outro ponto importante a notar é queshifting a number by one is equivalent to multiplying it by 2, or, in general, left shifting a number by n positions is equivalent to multiplication by 2^n.
Vamos tomar o valor 12 como valor de entrada.
Agora, vamos movê-lo 2 lugares para a esquerda (12 << 2) e ver qual será o resultado final.
O equivalente binário de 12 é 00001100. Depois de mudar para a esquerda por 2 casas, o resultado é 00110000, o equivalente a 48 em decimal:
@Test
public void givenOnePositiveInteger_whenLeftShiftOperator_thenNewDecimalNumber() {
int value = 12;
int leftShift = value << 2;
assertEquals(48, leftShift);
}
Isso funciona da mesma forma para um valor negativo:
@Test
public void givenOneNegativeInteger_whenLeftShiftOperator_thenNewDecimalNumber() {
int value = -12;
int leftShift = value << 2;
assertEquals(-48, leftShift);
}
4.2. Shift direito assinado [>>]
The right shift operator shifts all the bits to the right. O espaço vazio no lado esquerdo é preenchido dependendo do número de entrada:
-
Quando um número de entrada é negativo, onde o bit mais à esquerda é 1, os espaços vazios serão preenchidos com 1
-
Quando um número de entrada é positivo, onde o bit mais à esquerda é 0, os espaços vazios serão preenchidos com 0
Vamos continuar o exemplo usando 12 como entrada.
Agora, vamos movê-lo 2 lugares para a direita (12 >> 2) e ver qual será o resultado final.
O número de entrada é positivo; portanto, depois de mudar para a direita em 2 casas, o resultado é 0011, que é 3 em decimal:
@Test
public void givenOnePositiveInteger_whenSignedRightShiftOperator_thenNewDecimalNumber() {
int value = 12;
int rightShift = value >> 2;
assertEquals(3, rightShift);
}
Além disso, para um valor negativo:
@Test
public void givenOneNegativeInteger_whenSignedRightShiftOperator_thenNewDecimalNumber() {
int value = -12;
int rightShift = value >> 2;
assertEquals(-3, rightShift);
}
4.3. Deslocamento à direita não assinado [>>>]
Este operador é muito semelhante ao operador de turno certo assinado. The only difference is that the empty spaces in the left are filled with 0 irrespective of whether the number is positive or negative. Portanto, o resultado será sempre um número inteiro positivo.
Vamos deslocar para a direita o mesmo valor de 12:
@Test
public void givenOnePositiveInteger_whenUnsignedRightShiftOperator_thenNewDecimalNumber() {
int value = 12;
int unsignedRightShift = value >>> 2;
assertEquals(3, unsignedRightShift);
}
E agora, o valor negativo:
@Test
public void givenOneNegativeInteger_whenUnsignedRightShiftOperator_thenNewDecimalNumber() {
int value = -12;
int unsignedRightShift = value >>> 2;
assertEquals(1073741821, unsignedRightShift);
}
5. Diferença entre operadores bit a bit e lógicos
Existem algumas diferenças entre os operadores bit a bit que discutimos aqui e os operadores lógicos mais comumente conhecidos.
Primeiro,logical operators work on boolean expressionse retornam valoresboolean (true oufalse),, enquantobitwise operators work on binary digits de valores inteiros (long, int, short, char,ebyte) e retorna um inteiro.
Além disso, os operadores lógicos sempre avaliam a primeira expressãoboolean e, dependendo de seu resultado e do operador usado, podem ou não avaliar a segunda. Por outro lado,bitwise operators always evaluate both operands.
Finalmente, operadores lógicos são usados na tomada de decisões com base em várias condições, enquanto operadores bit a bit trabalham em bits e realizam operações bit a bit.
6. Casos de Uso
Alguns casos de uso em potencial de operadores bit a bit são:
-
Pilhas de comunicação em que os bits individuais no cabeçalho anexado aos dados significam informações importantes
-
Nos sistemas embarcados, para definir / limpar / alternar apenas um bit de um registro específico sem modificar os bits restantes
-
Para criptografar dados por problemas de segurança usando o operador XOR
-
Na compactação de dados, convertendo dados de uma representação para outra, para reduzir a quantidade de espaço usado
7. Conclusão
Neste tutorial, aprendemos sobre os tipos de operadores bit a bit e como eles são diferentes dos operadores lógicos. Também vimos alguns casos de uso em potencial para eles.
Todos os exemplos de código neste artigo estão disponíveisover on GitHub.