Operatori bit per bit Java

1. Panoramica

Gli operatori vengono utilizzati nel linguaggio Java per operare su dati e variabili.

In questo tutorial, esploreremo gli operatori bitwise e come funzionano in Java.

2. Operatori bit per bit

Gli operatori bit per bit lavorano su cifre binarie o bit di valori di input. Possiamo applicarli ai tipi interi: long, int, short, char e byte.

Prima di esplorare i diversi operatori bit per bit, capiamo prima come funzionano.

Gli operatori bit per bit lavorano su un equivalente binario di numeri decimali ed eseguono operazioni su di essi bit per bit secondo l'operatore dato:

  • Innanzitutto, gli operandi vengono convertiti nella loro rappresentazione binaria
  • Successivamente, l'operatore viene applicato a ciascun numero binario e il risultato viene calcolato
  • Infine, il risultato viene riconvertito nella sua rappresentazione decimale

Capiamo con un esempio; prendiamo due numeri interi:

int value1 = 6; int value2 = 5;

Successivamente, applichiamo un operatore OR bit per bit su questi numeri:

int result = 6 | 5;

Per eseguire questa operazione, innanzitutto, verrà calcolata la rappresentazione binaria di questi numeri:

Binary number of value1 = 0110 Binary number of value2 = 0101

Quindi l'operazione verrà applicata a ciascun bit. Il risultato restituisce un nuovo numero binario:

0110 0101 ----- 0111

Infine, il risultato 0111 verrà riconvertito in decimale che è uguale a 7 :

result : 7

Gli operatori bit per bit sono ulteriormente classificati come operatori logici bit per bit e operatori di spostamento bit per bit. Esaminiamo ora ciascun tipo.

3. Operatori logici bit per bit

Gli operatori logici bit per bit sono AND (&), OR (|), XOR (^) e NOT (~).

3.1. Bitwise OR (|)

L'operatore OR confronta ogni cifra binaria di due numeri interi e restituisce 1 se uno dei due è 1.

Questo è simile a || operatore logico utilizzato con booleani. Quando vengono confrontati due valori booleani, il risultato è vero se uno dei due è vero. Allo stesso modo, l'output è 1 quando uno dei due è 1.

Abbiamo visto un esempio di questo operatore nella sezione precedente:

@Test public void givenTwoIntegers_whenOrOperator_thenNewDecimalNumber()  value2; assertEquals(7, result); 

Vediamo la rappresentazione binaria di questa operazione:

0110 0101 ----- 0111

Qui, possiamo vedere che usando OR, 0 e 0 risulterà in 0, mentre qualsiasi combinazione con almeno 1 risulterà in 1.

3.2. AND bit per bit (&)

L'operatore AND confronta ogni cifra binaria di due numeri interi e restituisce 1 se entrambi sono 1, altrimenti restituisce 0.

È simile all'operatore && con valori booleani . Quando i valori di due booleani sono veri, il risultato di un'operazione && è vero.

Usiamo lo stesso esempio di sopra, tranne che ora usando l'operatore & invece di | operatore:

@Test public void givenTwoIntegers_whenAndOperator_thenNewDecimalNumber() { int value1 = 6; int value2 = 5; int result = value1 & value2; assertEquals(4, result); }

Vediamo anche la rappresentazione binaria di questa operazione:

0110 0101 ----- 0100

0100 è 4 in decimale, quindi il risultato è:

result : 4

3.3. XOR bit per bit (^)

L'operatore XOR confronta ogni cifra binaria di due numeri interi e restituisce 1 se entrambi i bit confrontati sono diversi. Ciò significa che se i bit di entrambi gli interi sono 1 o 0 il risultato sarà 0; in caso contrario, il risultato sarà 1:

@Test public void givenTwoIntegers_whenXorOperator_thenNewDecimalNumber() { int value1 = 6; int value2 = 5; int result = value1 ^ value2; assertEquals(3, result); }

E la rappresentazione binaria:

0110 0101 ----- 0011

0011 è 3 in decimale, quindi il risultato è:

result : 3

3.4. Bitwise COMPLEMENT (~)

L'operatore Bitwise Not o Complement indica semplicemente la negazione di ogni bit del valore di input. Richiede un solo numero intero ed è equivalente a! operatore.

Questo operatore cambia ogni cifra binaria dell'intero, il che significa che tutto 0 diventa 1 e tutto 1 diventa 0. Il! L'operatore funziona in modo simile per i valori booleani : inverte i valori booleani da vero a falso e viceversa.

Ora capiamo con un esempio come trovare il complemento di un numero decimale.

Facciamo il complemento di value1 = 6:

@Test public void givenOneInteger_whenNotOperator_thenNewDecimalNumber() { int value1 = 6; int result = ~value1; assertEquals(-7, result); }

Il valore in binario è:

value1 = 0000 0110

Applicando l'operatore di complemento, il risultato sarà:

0000 0110 -> 1111 1001

This is the one’s complement of the decimal number 6. And since the first (leftmost) bit is 1 in binary, it means that the sign is negative for the number that is stored.

Now, since the numbers are stored as 2’s complement, first we need to find its 2’s complement and then convert the resultant binary number into a decimal number:

1111 1001 -> 0000 0110 + 1 -> 0000 0111

Finally, 0000 0111 is 7 in decimal. Since the sign bit was 1 as mentioned above, therefore the resulting answer is:

result : -7

3.5. Bitwise Operator Table

Let's summarize the result of the operators we've seen to so far in a comparison table:

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. Bitwise Shift Operators

Binary shift operators shift all the bits of the input value either to the left or right based on the shift operator.

Let's see the syntax for these operators:

value  

The left side of the expression is the integer that is shifted, and the right side of the expression denotes the number of times that it has to be shifted.

Bitwise shift operators are further classified as bitwise left and bitwise right shift operators.

4.1. Signed Left Shift [<<]

The left shift operator shifts the bits to the left by the number of times specified by the right side of the operand. After the left shift, the empty space in the right is filled with 0.

Another important point to note is that shifting 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.

Let's take the value 12 as the input value.

Now, we will move it by 2 places to the left (12 <<2) and see what will be the final result.

The binary equivalent of 12 is 00001100. After shifting to the left by 2 places, the result is 00110000, which is equivalent to 48 in decimal:

@Test public void givenOnePositiveInteger_whenLeftShiftOperator_thenNewDecimalNumber() { int value = 12; int leftShift = value << 2; assertEquals(48, leftShift); } 

This works similarly for a negative value:

@Test public void givenOneNegativeInteger_whenLeftShiftOperator_thenNewDecimalNumber() { int value = -12; int leftShift = value << 2; assertEquals(-48, leftShift); }

4.2. Signed Right Shift [>>]

The right shift operator shifts all the bits to the right. The empty space in the left side is filled depending on the input number:

  • When an input number is negative, where the leftmost bit is 1, then the empty spaces will be filled with 1
  • When an input number is positive, where the leftmost bit is 0, then the empty spaces will be filled with 0

Let's continue the example using 12 as input.

Now, we will move it by 2 places to the right(12 >>2) and see what will be the final result.

The input number is positive, so after shifting to the right by 2 places, the result is 0011, which is 3 in decimal:

@Test public void givenOnePositiveInteger_whenSignedRightShiftOperator_thenNewDecimalNumber() { int value = 12; int rightShift = value >> 2; assertEquals(3, rightShift); }

Also, for a negative value:

@Test public void givenOneNegativeInteger_whenSignedRightShiftOperator_thenNewDecimalNumber() { int value = -12; int rightShift = value >> 2; assertEquals(-3, rightShift); }

4.3. Unsigned Right Shift [>>>]

This operator is very similar to the signed right shift operator. The only difference is that the empty spaces in the left are filled with 0 irrespective of whether the number is positive or negative. Therefore, the result will always be a positive integer.

Let's right shift the same value of 12:

@Test public void givenOnePositiveInteger_whenUnsignedRightShiftOperator_thenNewDecimalNumber() { int value = 12; int unsignedRightShift = value >>> 2; assertEquals(3, unsignedRightShift); }

And now, the negative value:

@Test public void givenOneNegativeInteger_whenUnsignedRightShiftOperator_thenNewDecimalNumber() { int value = -12; int unsignedRightShift = value >>> 2; assertEquals(1073741821, unsignedRightShift); }

5. Difference Between Bitwise and Logical Operators

There are a few differences between the bitwise operators we've discussed here and the more commonly known logical operators.

First, logical operators work on boolean expressions and return boolean values (either true or false), whereas bitwise operators work on binary digits of integer values (long, int, short, char, and byte) and return an integer.

Also, logical operators always evaluate the first boolean expression and, depending on its result and the operator used, may or may not evaluate the second. On the other hand, bitwise operators always evaluate both operands.

Finally, logical operators are used in making decisions based on multiple conditions, while bitwise operators work on bits and perform bit by bit operations.

6. Casi d'uso

Alcuni potenziali casi d'uso degli operatori bit per bit sono:

  • Stack di comunicazione in cui i singoli bit nell'intestazione allegata ai dati indicano informazioni importanti
  • Nei sistemi embedded per impostare / cancellare / alternare solo un singolo bit di un registro specifico senza modificare i bit rimanenti
  • Per crittografare i dati per problemi di sicurezza utilizzando l'operatore XOR
  • Nella compressione dei dati convertendo i dati da una rappresentazione all'altra, per ridurre la quantità di spazio utilizzato

7. Conclusione

In questo tutorial, abbiamo appreso i tipi di operatori bit per bit e come sono diversi dagli operatori logici. Abbiamo anche visto alcuni potenziali casi d'uso per loro.

Tutti gli esempi di codice in questo articolo sono disponibili su GitHub.