5. Operacje na bitach
Liczby całkowite (typów byte, short, int, long) są reprezentowane w pamięci
komputera w postaci binarnej jako ciągi bitów. Bit, który znajduje się w
takim zapisie najbardziej z lewej strony nazywa się bitem najstarszym (najbardziej znaczącym), a
ten najbardziej z prawej - najmłodszym (najmniej znaczącym). ![]()
Jak pamiętamy z wykładu 1 - dziesiętną wartość liczby zapisaną w systemie
dwójkowym możemy uzyskać sumując kolejne iloczyny wartości bitów (1 lub 0)
i odpowiednich potęg 2. Uwzględnienie liczb ujemnych wymaga, by wartość
bitu znaku "brana była" z minusem. Dlatego na powyższym rysunku: Taki zapis liczb całkowitych nazywa się zapisem z uzupełnieniem do dwóch Łatwo dostrzec, że z tego powodu wartość bezwzględna największej całkowitej liczby ujemnej jest zawsze większa od wartości bezwględnej największej całkowitej liczby dodatniej. W przypadku wartości typu byte najmniejsza możliwa wartość równa jest 100000000, czyli -1*27 = - 128, natomiast największa możliwa licznba dodatnia równa jest 01111111 = 127.
Pamiętamy też, że wartości typu char (reprezentujące znaki) mogą być traktowane
jak liczby całkowite. W tym przypadku jednak najstarszy bit nie jest traktowany
jako bit znaku - zatem wartości typu char (zajmujące 2 bajty) mogą kształtować
się w przedziale od 0 do 65536.
Każdy z argumentów wszystkich wymienionych operatorów bitowych musi być typu całkowitego. public class Test { public static void main(String[] args) { byte b = Byte.MAX_VALUE; System.out.println(b + " " + (b << 1)); System.out.println(b + " " + (byte) (b << 1)); int i = Integer.MAX_VALUE; System.out.println(i + " " + (i << 1)); } }
wyprowadzi:
Np. jeśli a1 = 5, a2 = 7, to logiczne operacje bitowe są przeprowadzane w następujący sposób:
Zwróćmy uwagę, że operacje te dotyczą tylko najmłodszych 3 bitów, niezależnie
od tego jakiego są typu i ile miejsca w pamięci zajmują argumenty a1 i a2.
public class Flags { public static void main(String[] args) { final int FL1 = 0x01, // stałe pokazujące które bity (flagi) mają FL2 = 0x02, // być testowane lub ustawiane FL3 = 0x04, FL4 = 0x08; int flags = 0; flags = flags | FL1; // ustawia flagę 1 (najmłodszy bit) show("flags = flags | FL1", flags); flags = 0; flags = flags | FL3; // ustawia flagę 3 (trzeci bit) show("flags = flags | FL3", flags); flags = 0; flags = flags | (FL1 | FL4); // ustawia 1 i 4 flagę show("flags = flags | (FL1 | FL4)", flags); // czy flaga 1 ustawiona if ((flags & FL1) > 0) System.out.println("Flaga 1 ustawiona"); else System.out.println("Flaga 1 NIE ustawiona"); // czy flaga 2 ustawiona if ((flags & FL2) > 0) System.out.println("Flaga 2 ustawiona"); else System.out.println("Flaga 2 NIE ustawiona"); // czy flagi 1 i 4 ustawione (jednoczesnie) int mask = FL1 | FL4; if ((flags & mask) == mask) System.out.println("Flagi 1 i 4 ustawione"); else System.out.println("Flagi 1 i 4 NIE ustawione"); // czy flagi 1 i 2 ustawione (jednoczesnie) mask = FL1 | FL2; if ((flags & mask) == mask) System.out.println("Flagi 1 i 2 ustawione"); else System.out.println("Flagi 1 i 2 NIE ustawione"); } static void show(String s, int flags) { System.out.println(s); char[] bits = Unspec.getBits(flags); System.out.println(new String(bits) + " --- val: " + flags); } }
Program wyprowadzi następujące informacje:
flags = flags | FL1 00000000000000000000000000000001 --- val: 1 flags = flags | FL3 00000000000000000000000000000100 --- val: 4 flags = flags | (FL1 | FL4) 00000000000000000000000000001001 --- val: 9 Flaga 1 ustawiona Flaga 2 NIE ustawiona Flagi 1 i 4 ustawione Flagi 1 i 2 NIE ustawione Proszę przeanalizować działanie programu i przetestować inne ustawienia flag. Przed lekturą dalszego tekstu proszę to zadanie rozwiązać samodzielnie
Możliwe rozwiązanie: public class Unspec { public static char[] getBits(byte v) { return getBits(8, (long) v); } public static char[] getBits(char v) { return getBits(16, (long) v); } public static char[] getBits(int v) { return getBits(32, (long) v); } public static char[] getBits(long v) { return getBits(64, v); } private static char[] getBits(int n, long v) { char[] bits = new char[n]; long mask = 1; for (int k = n-1; k >= 0; k--) { if ((v & mask) != 0) bits[k] = '1'; else bits[k] = '0'; mask = mask << 1; } return bits; } }
oraz program testujący, w którym - jako argumenty wywołania podajemy kolejno
liczbę typu long, liczbę typu int, znak, liczbę typu byte: class Test { public static void main(String[] args) { long l = Long.parseLong(args[0]); int i = Integer.parseInt(args[1]); char c = args[2].charAt(0); byte b = Byte.parseByte(args[3]); System.out.println("Wartość typu long " + l); System.out.println(new String(getBits(l))); l = Long.MAX_VALUE; System.out.println("Maksymalna wartość typu long " + l); System.out.println(new String(getBits(l))); l = Long.MIN_VALUE; System.out.println("Minimalna wartość typu long " + l); System.out.println(new String(getBits(l))); System.out.println("Wartość typu int " + i); System.out.println(new String(getBits(i))); System.out.println("Wartość typu char: znak " + "'" + c + "'" + ", kod " + (int) c); System.out.println(new String(getBits(c))); System.out.println("Wartość typu byte " + b); System.out.println(new String(getBits(b))); } }
Program ten - przy podanych argumentach: 1 -1 a -7 wyprowadzi:
Wartość typu long 1 0000000000000000000000000000000000000000000000000000000000000001 Maksymalna wartość typu long 9223372036854775807 0111111111111111111111111111111111111111111111111111111111111111 Minimalna wartość typu long -9223372036854775808 1000000000000000000000000000000000000000000000000000000000000000 Wartość typu int -1 11111111111111111111111111111111 Wartość typu char: znak 'a', kod 97 0000000001100001 Wartość typu byte -7 11111001
|