Operator bitwise bekerja pada susunan bit di dalam bilangan integer, bukan pada nilai desimalnya. Inilah alat ketika kita mengelola sekumpulan penanda on/off dalam satu angka, menghitung checksum, berkomunikasi dengan perangkat keras, atau mengatur permission ala Unix (rwx). Ada enam operatornya: &, |, ^, ~, <<, dan >>. Untuk melihat hasilnya dengan jelas, program pada Chapter ini mencetak bilangan dalam bentuk biner memakai std::format("{:b}", ...) — alat pemformatan teks dari header <format> yang dibahas tuntas pada Section Standard Library.

Enam Operator Bitwise #

Lima operator pertama memasangkan bit pada posisi yang sama dari dua operand (untuk ~, satu operand). Berikut aturan per pasangan bit, dengan bit 1 berarti menyala:

OperatorNamaAturan per bitContoh 0b1100 op 0b1010
&AND1 bila keduanya 10b1000
|OR1 bila salah satunya 10b1110
^XOR1 bila keduanya berbeda0b0110
~NOT (unary)membalik semua bitseluruh 32 bit terbalik
<<geser kiribit bergeser ke kiridiuji di bawah
>>geser kananbit bergeser ke kanandiuji di bawah

Litera 0b... adalah cara menulis bilangan biner langsung di kode, misalnya 0b1100 yang berarti 12 desimal. Bentuk ini juga sudah kita lihat pada Chapter Integer di Section Variabel dan Tipe Data.

cpp
#include <format>
#include <iostream>

int main() {
    std::cout << std::boolalpha;

    // std::format("{:b}", x) mencetak bilangan dalam bentuk biner;
    // alat pemformatan ini dibahas tuntas pada Section Standard Library.
    unsigned int a = 0b1100;   // 12 dalam desimal
    unsigned int b = 0b1010;   // 10 dalam desimal

    std::cout << "a = 0b" << std::format("{:b}", a) << "  (" << a << ")\n";
    std::cout << "b = 0b" << std::format("{:b}", b) << "  (" << b << ")\n\n";

    // Bitwise: bit di posisi yang sama dipasangkan
    std::cout << "a & b = 0b" << std::format("{:b}", a & b) << "  -> " << (a & b) << "\n";
    std::cout << "a | b = 0b" << std::format("{:b}", a | b) << "  -> " << (a | b) << "\n";
    std::cout << "a ^ b = 0b" << std::format("{:b}", a ^ b) << "  -> " << (a ^ b) << "\n";
    std::cout << "~a    = 0b" << std::format("{:b}", ~a) << "  -> " << ~a << "\n\n";

    // Shift: menggeser bit = kali/bagi pangkat dua
    unsigned int n = 6;
    std::cout << "6 << 2  = " << (n << 2) << "   (6 * 4)\n";
    std::cout << "24 >> 2 = " << ((n << 2) >> 2) << "   (24 / 4)\n\n";

    // Pola set / check / clear bit dengan mask
    unsigned int flag = 0b0000;
    unsigned int MASK = 0b0100;          // bit ke-2

    flag = flag | MASK;                  // set: nyalakan bit ke-2
    std::cout << "set bit ke-2     : 0b" << std::format("{:b}", flag) << "\n";
    std::cout << "bit ke-2 aktif?  : " << ((flag & MASK) != 0) << "\n";

    flag = flag & ~MASK;                 // clear: matikan bit ke-2
    std::cout << "clear bit ke-2   : 0b" << std::format("{:b}", flag) << "\n";
    std::cout << "bit ke-2 aktif?  : " << ((flag & MASK) != 0) << "\n";

    return 0;
}

Output yang Dihasilkan:

text
a = 0b1100  (12)
b = 0b1010  (10)

a & b = 0b1000  -> 8
a | b = 0b1110  -> 14
a ^ b = 0b110  -> 6
~a    = 0b11111111111111111111111111110011  -> 4294967283

6 << 2  = 24   (6 * 4)
24 >> 2 = 6   (24 / 4)

set bit ke-2     : 0b100
bit ke-2 aktif?  : true
clear bit ke-2   : 0b0
bit ke-2 aktif?  : false

Mari kita bedah:

  1. Baris 10-11: a diisi 0b1100 dan b diisi 0b1010; keduanya dicetak ulang dalam biner beserta nilai desimalnya agar mudah dicek.
  2. Baris 18-20 (&, |, ^): bandingkan tiap kolom bit. AND menyala hanya di posisi yang sama-sama menyala (1000), OR menyala bila salah satu menyala (1110), dan XOR menyala bila keduanya berbeda (0110).
  3. Baris 21 (~a): unary NOT membalik seluruh 32 bit milik unsigned int, sehingga muncul 28 angka 1 di depan; hasil desimalnya 4294967283. Ini memperlihatkan bahwa ~ bekerja pada semua bit, bukan hanya empat bit terakhir.
  4. Baris 25-26 (shift): 6 << 2 menggeser bit ke kiri dua langkah sehingga bernilai 24, lalu 24 >> 2 kembali ke 6.
  5. Baris 30-38 (mask): variabel MASK menandai bit ke-2. Pola set, check, dan clear bit dijelaskan pada bagian akhir Chapter.

Geser Bit: Kali dan Bagi Pangkat Dua #

Menggeser bit kiri n langkah sama hasilnya dengan mengalikan 2^n, dan menggeser kanan n langkah sama dengan membagi 2^n (bagian pecahan dibuang, seperti pembagian integer). Karena itu compiler sering menerjemahkan perkalian/divisi dengan pangkat dua menjadi satu instruksi shift — operasi yang sangat murah di CPU. Pola ini hanya berlaku untuk integer; untuk tipe floating-point tidak ada padanan shift-nya.

Pola Set, Check, dan Clear Bit #

Pola paling sering dipakai bitwise adalah mengelola flag: beberapa penanda on/off yang dikemas dalam satu integer. Setiap bit berdiri untuk satu penanda, dan MASK adalah konstanta yang menandai posisi bit yang sedang digarap. Dari output program di atas, tiga langkahnya terbaca jelas:

  • Set bit: flag | MASK menyalakan bit ke-2 tanpa menyentuh bit lain, hasilnya 0b100.
  • Check bit: (flag & MASK) != 0 menghasilkan true hanya bila bit ke-2 menyala. Hasil & dengan mask bisa dipakai langsung sebagai kondisi.
  • Clear bit: flag & ~MASK membalik mask (bit ke-2 jadi 0, sisanya 1) sehingga hanya bit ke-2 yang dipadamkan; hasilnya kembali 0b0.

Dengan tiga langkah ini kita bisa memakai satu unsigned int sebagai ladang penanda alih-alih delapan bool terpisah — teknik yang lazim di driver, jaringan, dan game. Untuk set bit pada posisi berapapun secara dinamis, kombinasi 1u << posisi membuat mask-nya on the fly.

Operator bitwise memilih nilai berdasarkan bit. Chapter berikutnya membahas operator yang juga memilih antara dua nilai, tetapi berdasarkan kondisi bool — operator ternary kondisi ? a : b.