Di level mesin, bilangan bulat adalah deretan bit. NumPy menyediakan ufunc bitwise untuk mengoperasikan deretan bit itu langsung per elemen, tanpa loop Python. Pemakaiannya nyata: sistem izin berkas gaya Unix yang menyimpan baca/tulis/eksekusi dalam tiga bit, pemadatan status hadir/tidak hadir menjadi satu byte, dan pembacaan pola biner dari data sensor.
Bab ini membahas empat operasi bitwise utama, pergeseran bit, np.packbits/np.unpackbits untuk memadatkan pola biner, serta np.bitwise_count milik NumPy 2.x. Semua fungsi ini berupa ufunc, sehingga bekerja elemen demi elemen dengan kecepatan C.
Empat Operasi Bitwise Utama #
np.bitwise_and, np.bitwise_or, np.bitwise_xor, dan np.bitwise_not membandingkan pasangan bit sesuai aturan logika masing-masing. Untuk memeriksa hasil dengan mata, np.binary_repr menampilkan bentuk biner sebuah bilangan:
import numpy as np
a = np.array([12, 10])
b = np.array([10, 3])
print(np.binary_repr(12, width=4), np.binary_repr(10, width=4), np.binary_repr(3, width=4))
print(np.bitwise_and(a, b))
print(np.bitwise_or(a, b))
print(np.bitwise_xor(a, b))Output yang Dihasilkan:
1100 1010 0011
[8 2]
[14 11]
[6 9]Baris demi baris:
- Baris 5 (
np.binary_repr): 12 ditampilkan sebagai1100, 10 sebagai1010, dan 3 sebagai0011. Padanan biner inilah acuan membaca tiga baris berikutnya. - Baris 6 (
np.bitwise_and): bit hasil bernilai 1 hanya bila kedua bit sumber bernilai 1:1100 & 1010 = 1000(8) dan1010 & 0011 = 0010(2), sehingga tercetak[8 2]. - Baris 7 (
np.bitwise_or): bit hasil bernilai 1 bila salah satu bit bernilai 1:1100 | 1010 = 1110(14) dan1010 | 0011 = 1011(11). - Baris 8 (
np.bitwise_xor): bit hasil bernilai 1 bila kedua bit berbeda:1100 ^ 1010 = 0110(6) dan1010 ^ 0011 = 1001(9).
Studi Kasus Flag Izin Berkas #
Izin berkas gaya Unix memakai tiga bit; satu bilangan menyimpan ketiganya sekaligus sebagai jumlah:
| Flag | Nilai | Biner | Makna |
|---|---|---|---|
| BACA | 4 | 100 | berkas boleh dibaca |
| TULIS | 2 | 010 | berkas boleh ditulis |
| EKSEKUSI | 1 | 001 | berkas boleh dieksekusi |
Nilai 7 (111) berarti baca+tulis+eksekusi, 6 (110) berarti baca+tulis tanpa eksekusi, dan 4 (100) berarti hanya baca. Tiga pola yang paling sering dipakai: memeriksa flag lewat bitwise_and, menambah flag lewat bitwise_or, dan mencabut flag lewat bitwise_and terhadap kebalikan bit dari bitwise_not:
import numpy as np
BACA, TULIS, EKSEKUSI = 4, 2, 1
izin = np.array([7, 6, 4, 5])
print(np.bitwise_and(izin, BACA) != 0)
print(np.bitwise_and(izin, EKSEKUSI) != 0)
print(np.bitwise_or(izin, TULIS))
print(np.bitwise_and(izin, np.bitwise_not(EKSEKUSI)))Output yang Dihasilkan:
[ True True True True]
[ True False False True]
[7 6 6 7]
[6 6 4 4]Baris demi baris:
- Baris 5:
np.bitwise_and(izin, BACA) != 0bernilai True persis bila bit baca menyala; keempat nilai (7, 6, 4, 5) semuanya memuat bit 4. - Baris 6: bit eksekusi hanya menyala pada 7 (
111) dan 5 (101); nilai 6 (110) dan 4 (100) tidak memuatnya. - Baris 7:
bitwise_ordengan TULIS menyalakan bit tulis tanpa menyentuh bit lain: 4 (100) menjadi 6 (110) dan 5 (101) menjadi 7 (111). - Baris 8:
np.bitwise_not(EKSEKUSI)membalik semua bit sehingga hanya bit eksekusi yang padam;bitwise_anddengan mask itu mencabut izin eksekusi: 7 menjadi 6, 6 tetap 6, 4 tetap 4, dan 5 menjadi 4.
Pergeseran Bit dengan left_shift dan right_shift #
np.left_shift(x, n) menggeser bit ke kiri sebanyak n posisi, setara dengan mengalikan dua pangkat n; np.right_shift menggeser ke kanan, setara pembagian bulat dua pangkat n. Pergeseran lazim dipakai untuk membangun nilai flag dari posisi bitnya:
import numpy as np
print(np.left_shift(np.array([1, 3, 5]), 2))
print(np.right_shift(np.array([16, 20, 7]), 2))
print(np.binary_repr(np.left_shift(3, 4), width=8))Output yang Dihasilkan:
[ 4 12 20]
[4 5 1]
00110000Baris demi baris:
- Baris 3: 1, 3, dan 5 digeser dua bit ke kiri: 1 menjadi
100(4), 3 (11) menjadi1100(12), dan 5 (101) menjadi10100(20). - Baris 4: 16, 20, dan 7 digeser dua bit ke kanan: 16 (
10000) menjadi 4, 20 (10100) menjadi 5, dan 7 (111) menjadi 1 karena pembagian bulat membulatkan ke bawah. - Baris 5: 3 digeser kiri empat bit menjadi 48;
binary_reprlebar 8 menampilkannya sebagai00110000.
Memadatkan Pola Biner dengan packbits dan unpackbits #
Delapan status 0/1 yang disimpan sebagai array int memakan delapan byte. np.packbits memadatkan deretan 0/1 menjadi bit di dalam byte, dan np.unpackbits membongkarnya kembali bit demi bit. Untuk array 2-D, parameter axis=1 memadatkan per baris:
import numpy as np
pola = np.array([1, 1, 0, 0, 1, 0, 1, 0])
terkemas = np.packbits(pola)
print(terkemas)
print(np.unpackbits(terkemas))
status = np.array([[1, 1, 0], [0, 1, 1]])
print(np.packbits(status, axis=1))Output yang Dihasilkan:
[202]
[1 1 0 0 1 0 1 0]
[[192]
[ 96]]Baris demi baris:
- Baris 4: deretan
11001010dibaca sebagai bilangan biner, menghasilkan satu byte bernilai 202. - Baris 6:
np.unpackbitsmengembalikan delapan bit byte itu dalam urutan semula,[1 1 0 0 1 0 1 0]. - Baris 8-9 (
axis=1): tiap baris 3 bit dilengkapi lima bit 0 di kanan sebelum dipadatkan:110menjadi11000000(192) dan011menjadi01100000(96).
Menghitung Bit Satu dengan np.bitwise_count #
NumPy 2.x menambahkan np.bitwise_count, yang menghitung banyaknya bit bernilai satu pada tiap elemen — operasi yang lazim disebut popcount. Salah satu pemakaian cepatnya adalah menguji paritas:
import numpy as np
nilai = np.array([5, 6, 7, 8])
bit_satu = np.bitwise_count(nilai)
print(bit_satu)
print(bit_satu % 2 == 0)Output yang Dihasilkan:
[2 2 3 1]
[ True True False False]Baris 4 menjelaskan segalanya: 5 (101) memuat dua bit satu, 6 (110) dua bit, 7 (111) tiga bit, dan 8 (1000) satu bit. Baris 6 lalu menguji paritas genap lewat sisa bagi 2; hasilnya [True True False False].
Satu pembeda penting dibanding bab tetangga: np.logical_and dan kawan-kawan pada Chapter Operasi Boolean menilai kebenaran tiap elemen secara utuh — nol dianggap salah — sedangkan operasi di bab ini menilai tiap bit di dalam elemen. Semua fungsi bab ini adalah ufunc yang menerima array seganda bentuk berkat broadcasting; mekanisme ufunc dibedah tuntas pada Chapter Fungsi Universal (ufunc).