Phép toán thao tác bit

Trong ngôn ngữ máy tính, các phép toán trên thao tác bit (tiếng Anh: bitwise operation) được thực hiện trên một hoặc nhiều chuỗi bit hoặc số nhị phân tại cấp độ của từng bit riêng biệt. Các phép toán này được thực hiện nhanh, ưu tiên, được hỗ trợ trực tiếp bởi vi xử lý, và được dùng để điều khiển các giá trị dùng cho so sánh và tính toán.
Đối với các loại vi xử lý rẻ tiền, thường thì các phép toán trên thao tác bit nhanh hơn phép chia đáng kể, đôi khi nhanh hơn phép nhân, và đôi khi nhanh hơn phép cộng đáng kể. Trong khi các vi xử lý hiện đại thường thực hiện phép nhân và phép cộng nhanh tương đương các phép toán trên thao tác bit nhờ vào cấu trúc đường ống lệnh của chúng dài hơn và cũng nhờ vào các lựa chọn trong thiết kế cấu trúc, các phép toán trên thao tác bit thường sử dụng ít năng lượng hơn vì sử dụng ít tài nguyên hơn.

Các toán tử thao tác bit

Các toán tử thao tác bit (tiếng Anh: bitwise operator) là các toán tử được sử dụng chung với một hoặc hai số nhị phân để tạo ra một phép toán thao tác bit. Hầu hết các toán tử thao tác bit đều là các toán tử một hoặc hai ngôi.
Trong các giải thích dưới đây, bất kỳ dấu hiện nào của vị trí một bit được tính từ phía bên phải (nhỏ nhất), tiến dần về bên trái. Ví dụ: số nhị phân 0001 (số một trong hệ thập phân) có các số 0 ở mọi vị trí trừ vị trí đầu tiên.

Dịch chuyển và quay bit

Các phép dịch chuyển bit đôi khi được xem là các phép toán thao tác bit, bởi vì chúng sẽ xem một giá trị dưới dạng một dãy bit hơn là dưới dạng số lượng số (numerial quantity). Trong các phép toán này, các chữ số sẽ được di chuyển, hoặc dịch chuyển, sang trái hoặc phải. Các thanh ghi trong vi xử lý máy tính có độ dài cố định, vì vậy một vài bit sẽ bị "dịch chuyển ra ngoài" thanh ghi ở một đầu, trong khi đó thì một lượng bit tương ứng sẽ được "dịch chuyển vào" ở đầu còn lại; sự khác biệt ở các phép toán dịch chuyển bit nằm ở chỗ cách chúng xác định giá trị của các bit được dịch chuyển vào.

Dịch chuyển số học

Trong dịch chuyển số học, các bit được dịch chuyển ra khỏi đầu hoặc đuôi sẽ bị loại bỏ. Trong phép dịch chuyển số học về bên trái, các số 0 được dịch chuyển vào bên phải; trong phép dịch chuyển số học bên phải, bit thể hiện dấu được thêm vào bên trái, do đó dấu của số được giữ nguyên.
Ví dụ dưới đây sử dụng thanh ghi 8-bit:
 00010111 (số thập phân +23) Dịch chuyển trái
= 00101110 (số thập phân +46)
 10010111 (số thập phân -105) Dịch chuyển phải
= 11001011 (số thập phân -53)
Trường hợp đầu tiên, những số tận cùng bên trái được dịch chuyển khỏi thanh ghi, một số 0 mới được thêm vào cuối bên phải của thanh ghi. Trường hợp thứ hai, thành phần cuối bên phải đã được dịch chuyển ra khỏi, và số 1 được thêm vào bên trái, bảo toàn được dấu của số. Nhiều lần dịch chuyển có thể được rút ngắn lại còn một lần. Ví dụ:
 00010111 (số thập phân +23) Dịch sang trái 2 lần.
= 01011100 (số thập phân +92)
Dịch chuyển số học bên trái n lần tương đương nhân với 2ⁿ (nếu giá trị đó không gây tràn bộ nhớ), trong khi đó thì phép dịch chuyển số học sang phải n lần của một giá trị bù 2 thì tương đương với việc chia cho 2ⁿ và làm tròn về phía âm vô cùng. Nếu số nhị phân được xem là bù 1, thì phép dịch chuyển sang phải tương tự sẽ cho kết quả bằng với việc chia số đó cho 2ⁿ và làm tròn về phía 0.

Quay có nhớ

Quay có nhớ tương tự với phép quay không nhớ, nhưng hai đầu của thanh ghi được tách ra bởi cờ nhớ (carry flag). Bit được dịch chuyển vào (ở bất kỳ đầu nào) là giá trị cũ của cờ nhớ, và bit được dịch chuyển ra (ở đầu còn lại) trở thành giá trị mới của cờ nhớ.
Một phép quay có nhớ có thể mô phỏng một phép quay luận lý hoặc số học của một vị trí bằng cách thiết lập cờ nhớ trước tiên. Ví dụ, nếu cờ nhớ mang giá trị 0, thì x XOAY-PHẢI-CÓ-NHỚ-MỘT-LẦN là phép dịch chuyển luận lý sang phải, và nếu cờ nhớ giữ giá trị của bản sao chép của bit chứa dấu, thì x XOAY-PHẢI-CÓ-NHỚ-MỘT-LẦNlà phép dịch chuyển số học sang phải. Vì lý do này, một số vi điều khiển như các PIC tầm thấp chỉ có xoay và xoay có nhớ, mà không cần đến các cấu trúc dịch chuyển số học và luận lý.

Dịch chuyển trong C, C++, C# và Python

Trong các ngôn ngữ dựa trên C, các toán tử dịch chuyển trái và phải lần lượt là << và >>. Số lượng cần dịch chuyển được cung cấp ở đối số thứ hai của toán tử dịch chuyển. Ví dụ:
x = y << 2;
gán cho x kết quả của phép dịch chuyển y sang trái 2 bit, tương đương với phép nhân với 4.
Trong ngôn ngữ C, kết quả của việc dịch chuyển sang phải một giá trị âm là xác định, và giá trị của phép dịch chuyển sang trái của giá trị chứa dấu là không xác định nếu kết quả không được thể hiện dưới dạng của kết quả. Trong C#, phép dịch chuyển sang phải là một phép dịch chuyển số học khi mà toán hạng là biến kiểu int hoặc long. Nếu toán hạng đầu tiên thuộc kiểu uint hoặc ulong, phép dịch chuyển sang phải là phép dịch chuyển luận lý.

Dịch chuyển trong Pascal

Trong Pascal, cũng như các trình biên dịch tương tự nó (như là Object Pascal và Standard Pascal), các thao tác dịch trái và dịch phải lần lượt là shl và shr. Khoảng cách dịch chuyển sẽ được thêm vào trong đối số thứ hai. Ví dụ, câu lệnh sau cho x là kết quả của phép dịch y sang trái hai bit:
x:= y shl 2;

Ứng dụng

Các phép toán trên thao tác bit là đặc biệt cần thiết trong các ngôn ngữ lập trình bậc thấp như các ngôn ngữ dùng để viết ra các trình cắm thiết bị (drivers), đồ họa bậc thấp, hình thành gói giao thức các truyền thông, và giải mã.
Mặc dù các hệ thống máy thường có sẵn các cấu trúc (instructions) hiệu quả cho việc thực hiện các phép toán học và phép luận lý (logic), tuy nhiên trong thực tế, các thao thác này có thể được thực hiện bằng cách kết hợp các toán tử thao tác bit và phép thử số 0 (zero-testing) bằng nhiều cách khác nhau. Ví dụ, dưới đây là mã giải (pseudocode) của phép nhân Ai Cập cổ đại chỉ ra cách để có thể nhân hai số nguyên tùy thích a và b (trong đó a lớn hơn b) mà chỉ cần sử dụng thao tác dịch chuyển bit và phép cộng:
c = 0

while b ≠ 0
  if (b and 1) ≠ 0
    c = c + a
  left shift a by 1
  right shift b by 1

return c
Một ví dụ nữa là mã giải của phép cộng, chỉ ra cách để tính tổng của hai số nguyên a và b sử dụng các toán tử thao tác bit và phép thử số 0:
while a ≠ 0
  c = b and a
  b = b xor a
  left shift c by 1
  a = c

return b
Lưu ý: các dấu = trong các ví dụ trên là phép gán chứ không phải là phép tương đương.