JavaRush /Blog Java /Random-PL /Operacje bitowe w Javie

Operacje bitowe w Javie

Opublikowano w grupie Random-PL
Prawdopodobnie znasz słowo „bicie”. Jeśli nie, poznajmy to :) Bit to minimalna jednostka miary informacji w komputerze. Jego nazwa pochodzi od angielskiego „ cyfry binarnej ” – „liczby binarnej”. Bit można wyrazić jedną z dwóch liczb: 1 lub 0. Istnieje specjalny system liczbowy oparty na zerach i jedynkach – binarny. Nie będziemy zagłębiać się w dżunglę matematyki i po prostu zauważymy, że dowolną liczbę w Javie można przekonwertować na jej postać binarną. Aby to zrobić, musisz użyć klas opakowujących. Operacje bitowe - 1Na przykład, oto jak to zrobić dla liczby int:
public class Main {

   public static void main(String[] args) {

       int x = 342;
       System.out.println(Integer.toBinaryString(x));
   }
}
Wyjście konsoli:

101010110
1010 10110 (dodałem spację dla czytelności) to liczba 342 w formacie binarnym. Właściwie podzieliliśmy tę liczbę na poszczególne bity - zera i jedyneki. To właśnie za ich pomocą możemy wykonywać operacje zwane bitowymi.
  • ~— operator bitowy „NIE”.

Działa to bardzo prosto: przechodzi przez każdy bit naszej liczby i zmienia jego wartość na przeciwną: zera na jedynki, jedynki na zera. Jeśli zastosujemy to do naszej liczby 342, otrzymamy następujący wynik: 101010110 to liczba 342 w formacie binarnym 010101001 to wynik wyrażenia ~342 Ale ponieważ zmienna int zajmuje 4 bajty, tj. 32 bity, w rzeczywistości liczba w zmiennej jest zapisywana jako: 00000000 00000000 00000001 01010110- liczba 342 w zmiennej typu int w Javie 11111111 11111111 11111110 10101001- wynik wyrażenia ~342 w Javie Spróbujmy to zrobić w praktyce:
public class Main {

   public static void main(String[] args) {

       int x = 342;
       System.out.println(Integer.toBinaryString(~x));
   }
}
Wyjście konsoli:
11111111111111111111111010101001
  • &— operator bitowy „AND”

Jak widać, jest on zapisany całkiem podobnie do logicznego „AND” ( &&). Operator &&, jak pamiętasz, zwraca truetylko wtedy, gdy oba operandy są prawdziwe. Bitwise &działa w podobny sposób: porównuje dwie liczby krok po kroku. Wynikiem tego porównania jest trzecia liczba. Przykładowo weźmy liczby 277 i 432: 100010101 – liczba 277 w postaci binarnej 110110000 – liczba 432 w postaci binarnej Następnie operator &porównuje pierwszy bit wyższej liczby z pierwszym bitem dolnej. Ponieważ jest to operator „AND”, wynik będzie równy 1 tylko wtedy, gdy oba bity będą równe 1. We wszystkich pozostałych przypadkach wynikiem będzie 0. 100010101 & 110110000 _______________ 100010000 - wynik pracy & Najpierw porównujemy pierwsze bity dwóch liczb ze sobą, następnie drugi bit, trzeci itd. Jak widać tylko w dwóch przypadkach oba bity były w liczbie równej 1 (bit pierwszy i piąty). Wynik wszystkich innych porównań wyniósł 0. Ostatecznie otrzymaliśmy liczbę 100010000. W systemie dziesiętnym odpowiada ona liczbie 272. Sprawdźmy:
public class Main {

   public static void main(String[] args) {
       System.out.println(277&432);
   }
}
Wyjście konsoli:

272
  • |- bitowe „OR”. Zasada działania jest taka sama – porównujemy dwie liczby krok po kroku. Dopiero teraz, jeśli chociaż jeden z bitów będzie równy 1, wynik będzie równy 1. Przyjrzyjmy się tym samym liczbom - 277 i 432:
100010101 | 110110000 _______________ 110110101 - wynik pracy | Tutaj wynik jest inny: tylko te bity, które w obu liczbach były zerami, pozostały zerami. Wynikiem pracy jest liczba 110110101. W systemie dziesiętnym odpowiada ona liczbie 437. Sprawdźmy:
public class Main {

   public static void main(String[] args) {
       System.out.println(277|432);
   }
}
Wyjście konsoli:

437
Wszystko policzyliśmy poprawnie! :)
  • ^- bitowe wyłączne OR (znane również jako XOR)
Nigdy wcześniej nie spotkaliśmy się z takim operatorem. Ale nie ma w tym nic skomplikowanego. Wygląda jak zwykłe „lub”. Różnica jest jedna: zwykłe „lub” zwraca, truejeśli przynajmniej jeden operand jest prawdziwy. Ale niekoniecznie jeden - jeśli są oba true- to wynik true. Ale wyłączne „lub” zwraca truetylko wtedy, gdy jeden z operandów jest prawdziwy. Jeśli oba operandy są prawdziwe, zwróci zwykłe „lub” true(„przynajmniej jeden jest prawdziwy”), ale wyłączny lub zwróci false. Dlatego nazywa się to ekskluzywnym. Znając zasadę poprzednich operacji bitowych, prawdopodobnie możesz bez problemu samodzielnie wykonać operację 277^432. Ale lepiej przemyślmy to jeszcze raz razem :) 100010101 ^ 110110000 _______________ 010100101 - wynik pracy ^ Oto nasz wynik. Te bity, które w obu liczbach były takie same, zwracały 0 (formuła „jeden z” nie zadziałała). Ale te, które utworzyły parę 0-1 lub 1-0, ostatecznie zamieniły się w jednostkę. W rezultacie otrzymaliśmy liczbę 010100101. W systemie dziesiętnym odpowiada ona liczbie 165. Zobaczmy, czy poprawnie obliczyliśmy:
public class Main {

   public static void main(String[] args) {
       System.out.println(277^432);
   }
}
Wyjście konsoli:

165
Super! Wszystko jest dokładnie tak, jak myśleliśmy :) Teraz czas na zapoznanie się z operacjami zwanymi przesunięciami bitowymi. Nazwa w zasadzie mówi sama za siebie. Weźmy jakąś liczbę i przesuńmy jej bity w lewo i prawo :) Zobaczmy jak to wygląda:

Przesuń w lewo

Przesunięcie bitów w lewo jest oznaczone znakiem << Przykład:
public class Main {

   public static void main(String[] args) {
       int x = 64;//oznaczający
       int y = 3;//ilość

       int z = (x << y);
       System.out.println(Integer.toBinaryString(x));
       System.out.println(Integer.toBinaryString(z));
   }
}
W tym przykładzie liczba x=64nazywana jest wartością. To właśnie jego fragmenty będziemy przesuwać. Przesuniemy bity w lewo (można to określić na podstawie kierunku znaku <<). W systemie binarnym liczba 64 = 1000000. Liczba y=3nazywana jest ilością. Ilość odpowiada na pytanie „o ile bitów w prawo/lewo należy przesunąć bity liczby x?” W naszym przykładzie przesuniemy je o 3 bity w lewo. Aby proces zmiany był bardziej przejrzysty, spójrzmy na zdjęcie. W naszym przykładzie używamy liczb typu int. Intzajmują 32 bity pamięci komputera. Tak wygląda nasza pierwotna liczba 64: Operacje bitowe - 2A teraz, w dosłownym tego słowa znaczeniu, bierzemy każdy z naszych bitów i przesuwamy go w lewo o 3 komórki: Operacje bitowe - 3Oto co otrzymaliśmy. Jak widać przesunęły się wszystkie nasze bity i dodano jeszcze 3 zera spoza zakresu. 3 - bo przesuwaliśmy o 3. Gdybyśmy przesuwali o 10, dodano by 10 zer. Zatem wyrażenie to x << yoznacza „przesuń bity liczby хy komórek w lewo”. Wynikiem naszego wyrażenia była liczba 1000000000, która w systemie dziesiętnym równa się 512. Sprawdźmy:
public class Main {

   public static void main(String[] args) {
       int x = 64;//oznaczający
       int y = 3;//ilość

       int z = (x << y);
       System.out.println(z);
   }
}
Wyjście konsoli:

512
Zgadza się! Teoretycznie bity można przesuwać w nieskończoność. Ale ponieważ mamy liczbę int, dostępne są tylko 32 komórki. Spośród nich 7 jest już zajętych przez liczbę 64 (1 000 000). Zatem jeśli wykonamy np. 27 przesunięć w lewo, nasza jedyna jednostka wyjdzie poza zasięg i „nadpisze”. Pozostaną tylko zera!
public class Main {

   public static void main(String[] args) {
       int x = 64;//oznaczający
       int y = 26;//ilość

       int z = (x << y);
       System.out.println(z);
   }
}
Wyjście konsoli:

0
Tak jak się spodziewaliśmy, ten wyszedł poza 32-bitowe komórki i zniknął. Otrzymaliśmy 32-bitową liczbę składającą się wyłącznie z zer. Naturalnie w systemie dziesiętnym odpowiada to 0. Prosta zasada zapamiętywania przesunięć w lewo: przy każdym przesunięciu w lewo liczba jest mnożona przez 2. Na przykład spróbujmy Operacje bitowe - 4obliczyć wynik wyrażenia bez obrazków z bitami. 111111111 << 3aby trzykrotnie pomnożyć liczbę 111111111 przez 2. W rezultacie otrzymamy 888888888. Napiszmy kod i sprawdźmy go:
public class Main {

   public static void main(String[] args) {
       System.out.println(111111111 << 3);
   }
}
Wyjście konsoli:

888888888

Prawe przesunięcia

Są one oznaczone znakiem >>. Robią to samo, tylko w innym kierunku! :) Nie wymyślajmy koła na nowo i spróbujmy to zrobić z tą samą liczbą int 64.
public class Main {

   public static void main(String[] args) {
       int x = 64;//oznaczający
       int y = 2;//ilość

       int z = (x >> y);
       System.out.println(z);
   }
}
Operacje bitowe - 5Operacje bitowe - 6W wyniku przesunięcia o 2 w prawo dwa skrajne zera naszej liczby wyszły poza zakres i zostały wymazane. Otrzymaliśmy liczbę 10000, która w systemie dziesiętnym odpowiada liczbie 16. Wyjście do konsoli:

16
Prosta zasada zapamiętywania przesunięć w prawo: każde przesunięcie w prawo dzieli się przez dwa, odrzucając resztę. Oznacza to na przykład, 35 >> 2 że musimy podzielić 35 przez 2 2 razy, odrzucając resztę 35/2 = 17(odrzucając resztę 1) 17:2 = 8(odrzucając resztę 1). Suma 35 >> 2powinna wynosić 8. Sprawdź:
public class Main {

   public static void main(String[] args) {
       System.out.println(35 >> 2);
   }
}
Wyjście konsoli:

8

Pierwszeństwo operacji w Javie

Pisząc lub czytając kod, często natkniesz się na wyrażenia, w których wykonywanych jest jednocześnie kilka operacji. Bardzo ważne jest, aby zrozumieć, w jakiej kolejności będą one wykonywane, w przeciwnym razie wynik może być nieoczekiwany. Ponieważ w Javie jest wiele operacji, wszystkie zostały rozdzielone w specjalnej tabeli:

Pierwszeństwo operatora

Operatorzy Precedens
przyrostek expr++ expr--
jednoargumentowy ++expr --expr +expr ~ !
Mnożny * / %
przyłączeniowy + -
zmiana << >> >>>
relacyjny < > <= >=wystąpienie
równość == !=
bitowe AND &
bitowo wyłączne OR ^
bitowo włącznie OR |
logiczne ORAZ &&
logiczne LUB ||
potrójny ? :
zadanie = += -= *= /= %= &= ^= |= <<= >>= >>>=
Wszystkie operacje wykonywane są od lewej do prawej, ale z uwzględnieniem ich priorytetu. Przykładowo, jeśli napiszemy: int x = 6 - 4/2; najpierw zostanie wykonana operacja dzielenia (4/2). Chociaż jest druga w kolejce, ma wyższy priorytet. Nawiasy lub nawiasy kwadratowe zmieniają dowolny priorytet na maksymalny. Pewnie pamiętacie to ze szkoły. Na przykład, jeśli dodasz je do wyrażenia: int x = (6 - 4)/2; najpierw zostanie wykonane odejmowanie, ponieważ jest ono obliczane w nawiasach. Operator logiczny ma &&raczej niski priorytet, jak widać z tabeli. Dlatego najczęściej będzie wykonywany jako ostatni. Na przykład: boolean x = 6 - 4/2 > 3 && 12*12 <= 119; To wyrażenie zostanie wykonane w następujący sposób:
  • 4/2 = 2

    boolean x = 6 - 2 > 3 && 12*12 <= 119;
  • 12*12 = 144

    boolean x = 6 - 2 > 3 && 144 <= 119;
  • 6-2 = 4

    boolean x = 4 > 3 && 144 <= 119;
  • Następnie zostaną wykonane operatory porównania:

    4 > 3 = true

    boolean x = true && 144 <= 119;
  • 144 <= 119 = false

    boolean x = true && false;
  • Na koniec zostanie wykonany ostatni operator &&.

    boolean x = true && false;

    boolean x = false;

    Na przykład operator dodawania ( +) ma wyższy priorytet niż operator porównania !=(„nie równy”);

    Dlatego w wyrażeniu:

    boolean x = 7 != 6+1;

    najpierw zostanie wykonana operacja 6+1, następnie sprawdzenie 7!=7 (fałsz), a na koniec wynik zostanie przypisany do falsezmiennej x. Przypisanie ma zazwyczaj najniższy priorytet ze wszystkich operacji - spójrz w tabelę.

Uff! Nasz wykład był długi, ale udało wam się! Jeśli nie rozumiesz w pełni niektórych fragmentów tego i poprzednich wykładów, nie martw się, będziemy poruszać te tematy jeszcze nie raz w przyszłości. Oto kilka przydatnych linków dla Ciebie:
  • Operatory logiczne - wykład JavaRush na temat operacji logicznych. Nieprędko do nich dotrzemy, ale już teraz możesz je przeczytać, nic ci się nie stanie
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION