เราคงคุ้นเคยกับตัวดำเนินการเลขคณิตอย่าง + – * / หรือ % และตัวดำเนินการเชิงตรรกะ เช่น & หรือ | แต่ยังมีตัวดำเนินการที่ใช้จัดการตัวเลขในระดับบิตเพราะทุกตัวเลขสุดท้ายแล้วจะถูกจัดเก็บในรูปแบบไบนารี นั่นคือ 0 และ 1 ซึ่งตัวดำเนินการสามารถจัดการได้ทั้งชนิดของข้อมูลแบบ byte short int long หรือแม้แต่ char

Bitwise : กลับบิตด้วยตัวดำเนินการ (~)

การกลับบิตหมายถึงเปลี่ยนจาก 0 เป็น 1 หรือจาก 1 เป็น 0 โดยใช้ตัวดำนินการ ~ ซึ่งเป็นตัวดำเนินการแบบ unary คือ ต้องการตัวถูกดำนินการเพียงตัวเดียว วิธีการใช้คือ ~ ตามด้วย ตัวถูกดำนินการ เช่น ~50 หรือ ~variable เป็นต้น

package com.company;

class Main {
    public static void main(String[] args) {
        System.out.println(42); // 00000000 00000000 00000000 00101010
        System.out.println(~42); // 11111111 11111111 11111111 11010101
        int a = 50;
        System.out.println(a);
        System.out.println(~a);
    }
}

42
-43  <-- 2's complement
50
-51  <-- 2's complement

Bitwise : ตัวดำเนินการ AND (&)

ตัวดำเนินการ and เป็นตัวดำเนินการที่ต้องการตัวถูกดำนินการ 2 ตัว โดยจะเปรียบเทียบบิตในตำแหน่งเดียวกันของตัวถูกดำเนินการแต่ละตัว ผลลัพธ์จากการ and จะเป็น 1 ก็ต่อเมื่อบิตที่นำมา and เป็น 1 ทั้งคู่

package com.company;

class Main {
    public static void main(String[] args) {
        System.out.println(1&2);
        // 00000000 00000000 00000000 00000001 & 00000000 00000000 00000000 00000010
    }
}

0

Bitwise : ตัวดำเนินการ OR (|)

ตัวดำเนินการ or เป็นตัวดำเนินการที่ต้องการตัวถูกดำนินการ 2 ตัว โดยจะเปรียบเทียบบิตในตำแหน่งเดียวกันของตัวถูกดำเนินการแต่ละตัว ผลลัพธ์จากการ orจะเป็น 0 ก็ต่อเมื่อบิตที่นำมา or เป็น 0 ทั้งคู่

package com.company;

class Main {
    public static void main(String[] args) {
        System.out.println(1|2);
        // 00000000 00000000 00000000 00000001 | 00000000 00000000 00000000 00000010
    }
}

3

Bitwise : ตัวดำเนินการ Exclisive OR – XOR (^)

ตัวดำเนินการ xor เป็นตัวดำเนินการที่ต้องการตัวถูกดำนินการ 2 ตัว โดยจะเปรียบเทียบบิตในตำแหน่งเดียวกันของตัวถูกดำเนินการแต่ละตัว ผลลัพธ์จากการ xor จะเป็น 1 ก็ต่อเมื่อบิตที่นำมา xor มีค่าต่างกัน

package com.company;

class Main {
    public static void main(String[] args) {
        System.out.println(1^2);
        // 00000000 00000000 00000000 00000001 ^ 00000000 00000000 00000000 00000010
    }
}

3

ตารางแสดงผลการดำเนินการในแบบต่างๆ

Bit shift: เลื่อนบิตไปทางซ้ายด้วย (<<)

การเลื่อนบิตไปทางซ้ายต้องการตัวดำเนินการ << และจำนวนบิตที่ต้องการเลื่อน เช่น 5<<3 เป็นการเลื่อนบิตของ 5 ไปทางซ้าย 3 ตำแหน่ง ข้อควรระวังคือจำนวนบิตที่ต้องการเลื่อนจะถูกนำไปคำนวนด้วย mobulo 32 จากตัวอย่างด้านล่าง modulo 35 คือ 3 จึงให้ผลลัพธ์เท่ากัน ดังนั้นในกรณี่เลื่อนบิตมากกว่า 9 ตำแหน่งควรตรวจสอบค่าที่ได้จากการคำนวนด้วย mobulo 32ก่อน อีกเรื่องหนึ่งคือการเลื่อนไป 1 ตำแหน่งเท่ากับการคูณด้วย 2 ดังนั้นการเลื่อนไป n ครั้งเท่ากับการคูณด้วย 2^n ดังนั้นในบางกรณีเราอาจจะใช้การเลื่อนบิตไปทางซ้ายแทนการคูณเพราะทำงานได้เร็วกว่ามาก แต่คนมาอ่านทีหลังอ่านแล้วจะเข้าใจยาก

package com.company;

class Main {
    public static void main(String[] args) {
        System.out.println(5<<3);
        // 00000000 00000000 00000000 00000101 < before
        // 00000000 00000000 00000000 00101000 < after
        System.out.println(5<<35);
    }
}

40
40

Bit shift: เลื่อนบิตไปทางขวาด้วย (>> และ >>>)

การเลื่อนบิตไปทางขวาจะซับซ้อนกว่าเล็กน้อยโดยมีทั้งแบบสนใจเครื่องหมายว่าจำนวนเป็นบวกหรือลบ (>>) และแบบที่ไม่สนใจเครื่องหมายว่าจำนวนเป็นบวกหรือลบ (>>>) จากตัวอย่างจะเห็นว่าในกรณีจำนวนเป็นบวกการใช้ >> หรือ >>> จะได้ผลลัพธ์เท่ากัน การเลื่อนบิตไปทางขวาจะเท่ากับการหารด้วย 2^n หรือ 2^n -1 หากเป็นเลขคี่

package com.company;

class Main {
    public static void main(String[] args) {
        System.out.println(256 >> 3);
        // 256 = 0000000100000000
        // 32  = 0000000000100000
        System.out.println(256 >>> 3);
        // -125 = 1111111110000000
        // -16  = 1111111111110000
        // 536870896 = 00011111111111111111111111110000
        System.out.println(-128 >> 3);
        System.out.println(-128 >>> 3);
    }
}

32
32
-16
536870896

ลำดับความสำคัญของตัวดำเนินการจะเรียงจากบนลงล่าง โดยด้านบนมีความสำคัญมากที่สุด และในบรรทัดเดียวกันจะเรียงลำดับจากซ้ายไปขวา