1. Общ преглед
Операторите се използват в езика Java за работа с данни и променливи.
В този урок ще изследваме Bitwise Operators и как работят в Java.
2. Побитови оператори
Побитовите оператори работят върху двоични цифри или битове на входни стойности. Можем да ги приложим към целочислените типове - long, int, short, char и byte.
Преди да проучим различните битови оператори, нека първо разберем как работят.
Побитовите оператори работят върху двоичен еквивалент на десетични числа и извършват операции върху тях бит по бит според дадения оператор:
- Първо, операндите се преобразуват в тяхното двоично представяне
- След това операторът се прилага към всяко двоично число и резултатът се изчислява
- Накрая резултатът се преобразува обратно в десетичното му представяне
Нека разберем с пример; да вземем две цели числа:
int value1 = 6; int value2 = 5;
След това нека приложим битов оператор ИЛИ на тези числа:
int result = 6 | 5;
За да извършите тази операция, първо ще бъде изчислено двоичното представяне на тези числа:
Binary number of value1 = 0110 Binary number of value2 = 0101
Тогава операцията ще бъде приложена към всеки бит. Резултатът връща ново двоично число:
0110 0101 ----- 0111
Накрая резултатът 0111 ще бъде преобразуван обратно в десетичен знак, който е равен на 7 :
result : 7
Побитовите оператори са допълнително класифицирани като побитови логически и побитови оператори на смяна. Нека сега разгледаме всеки тип.
3. Побитови логически оператори
Побитовите логически оператори са AND (&), OR (|), XOR (^) и NOT (~).
3.1. Побитово ИЛИ (|)
Операторът OR сравнява всяка двоична цифра от две цели числа и връща 1, ако някое от тях е 1.
Това е подобно на || логически оператор, използван с булеви числа. Когато се сравняват две булеви числа, резултатът е истина, ако някой от тях е истина. По същия начин изходът е 1, когато някой от тях е 1.
Видяхме пример за този оператор в предишния раздел:
@Test public void givenTwoIntegers_whenOrOperator_thenNewDecimalNumber() value2; assertEquals(7, result);
Нека видим двоичното представяне на тази операция:
0110 0101 ----- 0111
Тук можем да видим, че използването на OR, 0 и 0 ще доведе до 0, докато всяка комбинация с поне 1 ще доведе до 1.
3.2. Побитово И (&)
Операторът AND сравнява всяка двоична цифра от две цели числа и връща 1, ако и двете са 1, в противен случай връща 0.
Това е подобно на оператора && с булеви стойности. Когато стойностите на две булеви числа са верни, резултатът от операцията && е истина.
Нека използваме същия пример, както по-горе, с изключение на сега използването на оператора & вместо | оператор:
@Test public void givenTwoIntegers_whenAndOperator_thenNewDecimalNumber() { int value1 = 6; int value2 = 5; int result = value1 & value2; assertEquals(4, result); }
Нека видим и двоичното представяне на тази операция:
0110 0101 ----- 0100
0100 е 4 в десетичен знак, следователно резултатът е:
result : 4
3.3. Побитово XOR (^)
Операторът XOR сравнява всяка двоична цифра от две цели числа и връща 1, ако и двата сравнени бита са различни. Това означава, че ако битовете на двете цели числа са 1 или 0, резултатът ще бъде 0; в противен случай резултатът ще бъде 1:
@Test public void givenTwoIntegers_whenXorOperator_thenNewDecimalNumber() { int value1 = 6; int value2 = 5; int result = value1 ^ value2; assertEquals(3, result); }
И двоичното представяне:
0110 0101 ----- 0011
0011 е 3 в десетичен знак, следователно резултатът е:
result : 3
3.4. ПОБИТОВО ДОПЪЛНЕНИЕ (~)
Побитовият оператор Not или Complement просто означава отрицание на всеки бит от входната стойност. Отнема само едно цяло число и е еквивалентно на! оператор.
Този оператор променя всяка двоична цифра на цялото число, което означава, че всички 0 стават 1 и всички 1 стават 0. The! операторът работи по подобен начин за булеви стойности: той обръща булевите стойности от true на false и обратно.
Сега нека разберем с пример как да намерим комплемента на десетично число.
Нека направим допълнението на value1 = 6:
@Test public void givenOneInteger_whenNotOperator_thenNewDecimalNumber() { int value1 = 6; int result = ~value1; assertEquals(-7, result); }
Стойността в двоичен файл е:
value1 = 0000 0110
Чрез прилагане на оператора на комплемента резултатът ще бъде:
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. Използвайте калъфи
Някои потенциални случаи на използване на битови оператори са:
- Комуникационни стекове, където отделните битове в заглавката, прикрепени към данните, означават важна информация
- Във вградени системи за задаване / изчистване / превключване само на един бит от определен регистър, без да се променят останалите битове
- За криптиране на данни за проблеми с безопасността с помощта на оператора XOR
- При компресиране на данни чрез преобразуване на данни от едно представяне в друго, за да се намали количеството на използваното пространство
7. Заключение
В този урок научихме за видовете битови оператори и как те се различават от логическите оператори. Видяхме и някои потенциални случаи на употреба за тях.
Всички примери за кодове в тази статия са достъпни в GitHub.