Một số trường hợp sử dụng trong thế giới thực của các toán tử bitwise sau đây là gì?
- VÀ
- XOR
- KHÔNG PHẢI
- HOẶC LÀ
- Ca trái / phải
Một số trường hợp sử dụng trong thế giới thực của các toán tử bitwise sau đây là gì?
Câu trả lời:
Các trường bit (cờ)
Chúng là cách hiệu quả nhất để thể hiện một cái gì đó có trạng thái được xác định bởi một số thuộc tính "có hoặc không". ACL là một ví dụ tốt; nếu bạn có 4 quyền riêng biệt (đọc, viết, thực thi, thay đổi chính sách), tốt hơn hết là lưu trữ tệp này trong 1 byte thay vì lãng phí 4. Chúng có thể được ánh xạ thành các loại liệt kê bằng nhiều ngôn ngữ để thuận tiện hơn.
Giao tiếp qua cổng / ổ cắm
Luôn liên quan đến tổng kiểm, chẵn lẻ, bit dừng, thuật toán điều khiển luồng, v.v., thường phụ thuộc vào giá trị logic của từng byte thay vì giá trị số, vì phương tiện chỉ có thể truyền một bit tại một thời gian.
Nén, mã hóa
Cả hai điều này phụ thuộc rất nhiều vào thuật toán bitwise. Nhìn vào thuật toán deflate cho một ví dụ - mọi thứ đều tính bằng bit, không phải byte.
Máy trạng thái hữu hạn
Tôi đang nói chủ yếu về loại được nhúng trong một số phần cứng, mặc dù chúng cũng có thể được tìm thấy trong phần mềm. Đây là những tổ hợp trong tự nhiên - họ theo nghĩa đen có thể nhận được "biên soạn" xuống đến một loạt các cổng logic, vì vậy họ phải được diễn tả như AND
, OR
, NOT
vv
Đồ họa
Hầu như không có đủ không gian ở đây để đi vào mọi khu vực nơi các toán tử này được sử dụng trong lập trình đồ họa. XOR
(hoặc ^
) đặc biệt thú vị ở đây vì áp dụng cùng một đầu vào lần thứ hai sẽ hoàn tác lần đầu tiên. GUI cũ hơn được sử dụng để dựa vào điều này để làm nổi bật lựa chọn và các lớp phủ khác, để loại bỏ sự cần thiết phải vẽ lại tốn kém. Chúng vẫn hữu ích trong các giao thức đồ họa chậm (tức là máy tính để bàn từ xa).
Đó chỉ là một vài ví dụ đầu tiên tôi nghĩ ra - đây không phải là một danh sách đầy đủ.
Có lạ không?
(value & 0x1) > 0
Là nó chia hết cho hai (chẵn)?
(value & 0x1) == 0
Đây là một số thành ngữ phổ biến xử lý các cờ được lưu trữ dưới dạng các bit riêng lẻ.
enum CDRIndicators {
Local = 1 << 0,
External = 1 << 1,
CallerIDMissing = 1 << 2,
Chargeable = 1 << 3
};
unsigned int flags = 0;
Đặt cờ có thể tính phí:
flags |= Chargeable;
Xóa cờ CallerIDMissing:
flags &= ~CallerIDMissing;
Kiểm tra xem CallerIDMissing và Chargeable có được đặt hay không:
if((flags & (CallerIDMissing | Chargeable )) == (CallerIDMissing | Chargeable)) {
}
Tôi đã sử dụng các thao tác bitwise trong việc triển khai mô hình bảo mật cho CMS. Nó có các trang mà người dùng có thể truy cập nếu họ ở trong các nhóm thích hợp. Một người dùng có thể ở nhiều nhóm, vì vậy chúng tôi cần kiểm tra xem có sự giao thoa giữa các nhóm người dùng và các nhóm trang không. Vì vậy, chúng tôi đã gán cho mỗi nhóm một mã định danh sức mạnh 2 duy nhất, ví dụ:
Group A = 1 --> 00000001
Group B = 2 --> 00000010
Group C = 3 --> 00000100
Chúng tôi HOẶC các giá trị này cùng nhau và lưu trữ giá trị (dưới dạng một int) với trang. Ví dụ: nếu một nhóm A & B có thể được truy cập một trang, chúng tôi lưu trữ giá trị 3 (trong nhị phân là 00000011) làm điều khiển truy cập trang. Theo cùng một cách, chúng tôi lưu trữ một giá trị định danh nhóm ORed với người dùng để thể hiện họ thuộc nhóm nào.
Vì vậy, để kiểm tra xem một người dùng nhất định có thể truy cập một trang nhất định hay không, bạn chỉ cần VÀ các giá trị với nhau và kiểm tra xem giá trị có khác không. Điều này rất nhanh vì kiểm tra này được thực hiện trong một hướng dẫn duy nhất, không lặp, không có cơ sở dữ liệu khứ hồi.
Lập trình cấp thấp là một ví dụ tốt. Ví dụ, bạn có thể cần ghi một bit cụ thể vào thanh ghi ánh xạ bộ nhớ để làm cho một số phần cứng làm những gì bạn muốn:
volatile uint32_t *register = (volatile uint32_t *)0x87000000;
uint32_t value;
uint32_t set_bit = 0x00010000;
uint32_t clear_bit = 0x00001000;
value = *register; // get current value from the register
value = value & ~clear_bit; // clear a bit
value = value | set_bit; // set a bit
*register = value; // write it back to the register
Ngoài ra, htonl()
và htons()
được thực hiện bằng cách sử dụng&
và các |
toán tử (trên các máy có tuổi thọ (thứ tự Byte) không khớp với thứ tự mạng):
#define htons(a) ((((a) & 0xff00) >> 8) | \
(((a) & 0x00ff) << 8))
#define htonl(a) ((((a) & 0xff000000) >> 24) | \
(((a) & 0x00ff0000) >> 8) | \
(((a) & 0x0000ff00) << 8) | \
(((a) & 0x000000ff) << 24))
htons()
và htonl()
là các hàm POSIX để hoán đổi a short
hoặc a long
từ h
endianness của máy chủ ( ) sang n
thứ tự byte của mạng ( ).
htonl()
có int
giá trị 32 bit ? long
có nghĩa là 64 bit trong nhiều ngôn ngữ.
Tôi sử dụng chúng để lấy các giá trị RGB (A) từ các giá trị màu được đóng gói, chẳng hạn.
(a & b) >> c
nhanh hơn gấp 5 lần a % d / e
(cả hai cách để trích xuất một giá trị màu duy nhất từ một int đại diện cho ARGB). Tương ứng, 6,7 và 35,2 cho 1 tỷ lần lặp.
%
không phải là toán tử Modulus, nó là toán tử còn lại. Chúng tương đương với các giá trị dương nhưng khác với giá trị âm. Nếu bạn cung cấp các hạn chế thích hợp (vượt qua uint
thay vì int
ví dụ) thì hai ví dụ sẽ có cùng tốc độ.
Khi tôi có một loạt các cờ boolean, tôi muốn lưu trữ tất cả chúng trong một int.
Tôi lấy chúng ra bằng bitwise-AND. Ví dụ:
int flags;
if (flags & 0x10) {
// Turn this feature on.
}
if (flags & 0x08) {
// Turn a second feature on.
}
Vân vân.
if (flags.feature_one_is_one) { // turn on feature }
. Đó là trong tiêu chuẩn ANSI C, vì vậy tính di động không phải là một vấn đề.
& = AND:
Che dấu các bit cụ thể.
Bạn đang xác định các bit cụ thể sẽ được hiển thị hoặc không được hiển thị. 0x0 & x sẽ xóa tất cả các bit trong một byte trong khi 0xFF sẽ không thay đổi x. 0x0F sẽ hiển thị các bit ở mức thấp hơn.
Chuyển đổi:
Để truyền các biến ngắn hơn thành các biến dài hơn có nhận dạng bit, cần điều chỉnh các bit vì -1 trong một int là 0xFFFFFFFF trong khi -1 trong một dài là 0xFFFFFFFFFFFFFFFF. Để giữ bản sắc, bạn áp dụng mặt nạ sau khi chuyển đổi.
| = HOẶC
Đặt bit. Các bit sẽ được đặt độc lập nếu chúng đã được đặt. Nhiều cơ sở dữ liệu (bitfield) có các cờ như IS_HSET = 0, IS_VSET = 1 có thể được đặt độc lập. Để đặt cờ, bạn áp dụng IS_HSET | IS_VSET (Trong C và lắp ráp, điều này rất thuận tiện để đọc)
^ = XOR
Tìm các bit giống nhau hoặc khác nhau.
~ = KHÔNG
Lật bit.
Nó có thể được chỉ ra rằng tất cả các hoạt động bit cục bộ có thể có thể được thực hiện bởi các hoạt động này. Vì vậy, nếu bạn thích, bạn có thể thực hiện một lệnh ADD chỉ bằng các thao tác bit.
Một số hack tuyệt vời:
http://www.ugcs.caltech.edu/~wnoise/base2.html
http://www.jjj.de/bitwizardry/bitwizardrypage.html
= ~
, không |=
, đó là HOẶC.
& = AND
- Tại sao tôi muốn xóa tất cả các bit, tại sao tôi lại muốn có một phiên bản chưa sửa đổi của byte và tôi phải làm gì với nibble thấp hơn?
xor
chính nó. Tôi có thể nghĩ ra khá nhiều lý do mà bạn có thể muốn trích xuất phần thấp hơn. Đặc biệt nếu nibble thấp hơn đó là một phần của cấu trúc dữ liệu và bạn muốn sử dụng nó làm mặt nạ hoặc OR
với cấu trúc khác.
Mã hóa là tất cả các hoạt động bitwise.
Đây là một ví dụ để đọc màu từ hình ảnh bitmap ở định dạng byte
byte imagePixel = 0xCCDDEE; /* Image in RRGGBB format R=Red, G=Green, B=Blue */
//To only have red
byte redColour = imagePixel & 0xFF0000; /*Bitmasking with AND operator */
//Now, we only want red colour
redColour = (redColour >> 24) & 0xFF; /* This now returns a red colour between 0x00 and 0xFF.
Tôi hy vọng ví dụ nhỏ này sẽ giúp ....
Trong thế giới trừu tượng của ngôn ngữ hiện đại ngày nay, không quá nhiều. Tệp IO là một cách dễ dàng xuất hiện trong đầu, mặc dù đó là việc thực hiện các thao tác bitwise trên một cái gì đó đã được triển khai và không thực hiện một cái gì đó sử dụng các hoạt động bitwise. Tuy nhiên, như một ví dụ dễ hiểu, mã này thể hiện việc loại bỏ thuộc tính chỉ đọc trên một tệp (để nó có thể được sử dụng với FileStream mới chỉ định FileMode.Create) trong c #:
//Hidden files posses some extra attibutes that make the FileStream throw an exception
//even with FileMode.Create (if exists -> overwrite) so delete it and don't worry about it!
if(File.Exists(targetName))
{
FileAttributes attributes = File.GetAttributes(targetName);
if ((attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
File.SetAttributes(targetName, attributes & (~FileAttributes.ReadOnly));
File.Delete(targetName);
}
Theo như triển khai tùy chỉnh, đây là một ví dụ gần đây: Tôi đã tạo một "trung tâm thông báo" để gửi tin nhắn an toàn từ một cài đặt ứng dụng phân tán của chúng tôi sang ứng dụng khác. Về cơ bản, nó tương tự như email, hoàn chỉnh với Hộp thư đến, Hộp thư đi, Đã gửi, v.v., nhưng nó cũng được phân phối đảm bảo với biên nhận đã đọc, do đó, có thêm các thư mục con ngoài "hộp thư đến" và "đã gửi". Số tiền này là một yêu cầu đối với tôi để định nghĩa chung chung "trong hộp thư đến" hoặc "trong thư mục đã gửi" là gì. Trong thư mục đã gửi, tôi cần biết những gì đã đọc và những gì chưa đọc. Trong số những gì chưa đọc, tôi cần biết những gì đã nhận và những gì không nhận được. Tôi sử dụng thông tin này để xây dựng một mệnh đề động trong đó lọc một nguồn dữ liệu cục bộ và hiển thị thông tin phù hợp.
Đây là cách enum được kết hợp với nhau:
public enum MemoView :int
{
InboundMemos = 1, // 0000 0001
InboundMemosForMyOrders = 3, // 0000 0011
SentMemosAll = 16, // 0001 0000
SentMemosNotReceived = 48, // 0011
SentMemosReceivedNotRead = 80, // 0101
SentMemosRead = 144, // 1001
Outbox = 272, //0001 0001 0000
OutBoxErrors = 784 //0011 0001 0000
}
Bạn có thấy những gì nó làm? Bằng cách anding (&) với giá trị enum "Hộp thư đến", InboundMemos, tôi biết rằng InboundMemosForMyOrder nằm trong hộp thư đến.
Đây là phiên bản rút gọn của phương thức xây dựng và trả về bộ lọc xác định chế độ xem cho thư mục hiện được chọn:
private string GetFilterForView(MemoView view, DefaultableBoolean readOnly)
{
string filter = string.Empty;
if((view & MemoView.InboundMemos) == MemoView.InboundMemos)
{
filter = "<inbox filter conditions>";
if((view & MemoView.InboundMemosForMyOrders) == MemoView.InboundMemosForMyOrders)
{
filter += "<my memo filter conditions>";
}
}
else if((view & MemoView.SentMemosAll) == MemoView.SentMemosAll)
{
//all sent items have originating system = to local
filter = "<memos leaving current system>";
if((view & MemoView.Outbox) == MemoView.Outbox)
{
...
}
else
{
//sent sub folders
filter += "<all sent items>";
if((view & MemoView.SentMemosNotReceived) == MemoView.SentMemosNotReceived)
{
if((view & MemoView.SentMemosReceivedNotRead) == MemoView.SentMemosReceivedNotRead)
{
filter += "<not received and not read conditions>";
}
else
filter += "<received and not read conditions>";
}
}
}
return filter;
}
Vô cùng đơn giản, nhưng việc thực hiện gọn gàng ở mức độ trừu tượng thường không yêu cầu các thao tác bitwise.
Mã hóa Base64 là một ví dụ. Mã hóa Base64 được sử dụng để thể hiện dữ liệu nhị phân dưới dạng ký tự có thể in được để gửi qua hệ thống email (và các mục đích khác). Mã hóa Base64 chuyển đổi một chuỗi các byte 8 bit thành các chỉ mục tra cứu ký tự 6 bit. Các hoạt động bit, dịch chuyển, và, hoặc không, rất hữu ích để thực hiện các hoạt động bit cần thiết cho mã hóa và giải mã Base64.
Tất nhiên đây chỉ là 1 trong vô số ví dụ.
Tôi ngạc nhiên không ai chọn câu trả lời rõ ràng cho thời đại Internet. Tính toán địa chỉ mạng hợp lệ cho một mạng con.
Thông thường các thao tác bitwise nhanh hơn so với thực hiện nhân / chia. Vì vậy, nếu bạn cần nhân một biến x với 9, bạn sẽ thực hiện x<<3 + x
nhanh hơn một vài chu kỳx*9
. Nếu mã này nằm trong ISR, bạn sẽ tiết kiệm được thời gian phản hồi.
Tương tự như vậy nếu bạn muốn sử dụng một mảng như một hàng đợi tròn, nó sẽ nhanh hơn (và thanh lịch hơn) để xử lý các gói kiểm tra xung quanh với các thao tác khôn ngoan. (kích thước mảng của bạn phải là lũy thừa 2). Ví dụ :, bạn có thể sử dụng tail = ((tail & MASK) + 1)
thay vìtail = ((tail +1) < size) ? tail+1 : 0
, nếu bạn muốn chèn / xóa.
Ngoài ra nếu bạn muốn một cờ lỗi giữ nhiều mã lỗi cùng nhau, mỗi bit có thể giữ một giá trị riêng. Bạn có thể VÀ nó với mỗi mã lỗi riêng lẻ dưới dạng kiểm tra. Điều này được sử dụng trong mã lỗi Unix.
Ngoài ra một bitmap n-bit có thể là một cấu trúc dữ liệu thực sự tuyệt vời và nhỏ gọn. Nếu bạn muốn phân bổ một nhóm tài nguyên có kích thước n, chúng ta có thể sử dụng một bit n để biểu thị trạng thái hiện tại.
Không ai có vẻ đã đề cập đến toán học điểm cố định.
(Vâng, tôi già rồi, được chứ?)
Là một số x
có sức mạnh bằng 2? (Ví dụ, hữu ích trong các thuật toán tăng bộ đếm và một hành động chỉ được thực hiện với số lần logarit)
(x & (x - 1)) == 0
Đó là bit cao nhất của một số nguyên x
? (Ví dụ, điều này có thể được sử dụng để tìm công suất tối thiểu là 2 lớn hơn x
)
x |= (x >> 1);
x |= (x >> 2);
x |= (x >> 4);
x |= (x >> 8);
x |= (x >> 16);
return x - (x >>> 1); // ">>>" is unsigned right shift
Đó là 1
bit thấp nhất của một số nguyên x
? (Giúp tìm số lần chia hết cho 2.)
x & -x
x & -x
.
Toán tử bitwise rất hữu ích cho việc lặp các mảng có độ dài bằng 2. Như nhiều người đã đề cập, toán tử bitwise cực kỳ hữu ích và được sử dụng trong Cờ , Đồ họa , Mạng , Mã hóa . Không chỉ vậy, nhưng chúng cực kỳ nhanh. Sử dụng yêu thích cá nhân của tôi là lặp một mảng mà không có điều kiện . Giả sử bạn có một mảng dựa trên chỉ mục 0 (ví dụ: chỉ mục của phần tử đầu tiên là 0) và bạn cần lặp nó vô thời hạn. Bởi vô thời hạn tôi có nghĩa là đi từ yếu tố đầu tiên đến cuối cùng và trở lại đầu tiên. Một cách để thực hiện điều này là:
int[] arr = new int[8];
int i = 0;
while (true) {
print(arr[i]);
i = i + 1;
if (i >= arr.length)
i = 0;
}
Đây là phương pháp đơn giản nhất, nếu bạn muốn tránh nếu tuyên bố, bạn có thể sử dụng mô đun cách tiếp cận như vậy:
int[] arr = new int[8];
int i = 0;
while (true) {
print(arr[i]);
i = i + 1;
i = i % arr.length;
}
Mặt trái của hai phương thức này là toán tử mô đun là đắt tiền, vì nó tìm kiếm phần còn lại sau khi chia số nguyên. Và phương pháp đầu tiên chạy một nếu tuyên bố trên mỗi lần lặp. Tuy nhiên, với toán tử bitwise nếu độ dài của mảng của bạn là lũy thừa bằng 2, bạn có thể dễ dàng tạo ra một chuỗi như 0 .. length - 1
bằng cách sử dụng &
toán tử (bitwise và) như vậy i & length
. Vì vậy, biết điều này, mã từ trên trở thành
int[] arr = new int[8];
int i = 0;
while (true){
print(arr[i]);
i = i + 1;
i = i & (arr.length - 1);
}
Đây là cách nó làm việc. Trong định dạng nhị phân, mọi số có sức mạnh bằng 2 trừ đi 1 chỉ được biểu thị bằng số. Ví dụ 3 trong nhị phân là 11
, 7 là 111
, 15 là 1111
như vậy, bạn có ý tưởng. Bây giờ, điều gì xảy ra nếu bạn có &
bất kỳ số nào so với một số chỉ bao gồm các số trong nhị phân? Hãy nói rằng chúng tôi làm điều này:
num & 7;
Nếu num
nhỏ hơn hoặc bằng 7 thì kết quả sẽ là num
do mỗi bit &
-ed với 1 là chính nó. Nếu num
lớn hơn 7, trong quá trình &
vận hành, máy tính sẽ xem xét các số 0 đứng đầu, tất nhiên sẽ vẫn là số không sau khi &
hoạt động, phần duy nhất sẽ vẫn còn. Giống như trong trường hợp 9 & 7
nhị phân, nó sẽ trông giống như
1001 & 0111
kết quả sẽ là 0001 là 1 trong số thập phân và xử lý phần tử thứ hai trong mảng.
nó cũng có thể có ích trong mô hình quan hệ sql, giả sử bạn có các bảng sau: BlogEntry, BlogC Category
theo truyền thống, bạn có thể tạo mối quan hệ nn giữa chúng bằng bảng BlogEntryC Category hoặc khi không có nhiều bản ghi BlogC Category bạn có thể sử dụng một giá trị trong BlogEntry để liên kết với nhiều bản ghi BlogCarget giống như bạn làm với các enum được gắn cờ, trong hầu hết RDBMS cũng có một toán tử rất nhanh để chọn trên cột 'được gắn cờ' đó ...
Khi bạn chỉ muốn thay đổi một số bit của Đầu ra của vi điều khiển, nhưng thanh ghi để ghi là một byte, bạn làm một cái gì đó như thế này (mã giả):
char newOut = OutRegister & 0b00011111 //clear 3 msb's
newOut = newOut | 0b10100000 //write '101' to the 3 msb's
OutRegister = newOut //Update Outputs
Tất nhiên, nhiều bộ vi điều khiển cho phép bạn thay đổi từng bit riêng lẻ ...
Nếu bạn muốn tính mod số (%) của mình với công suất nhất định là 2, bạn có thể sử dụng yourNumber & 2^N-1
, trong trường hợp này là giống như yourNumber % 2^N
.
number % 16 = number & 15;
number % 128 = number & 127;
Điều này có lẽ chỉ hữu ích khi là một giải pháp thay thế cho hoạt động mô đun với cổ tức rất lớn là 2 ^ N ... Nhưng ngay cả khi đó tốc độ tăng tốc của nó đối với hoạt động mô đun là không đáng kể trong thử nghiệm của tôi trên .NET 2.0. Tôi nghi ngờ trình biên dịch hiện đại đã thực hiện tối ưu hóa như thế này. Có ai biết thêm về điều này không?
%
hoạt động còn lại, họ xử lý các tiêu cực khác nhau. Tuy nhiên, nếu bạn chuyển uint
đến %
, trình biên dịch C # sẽ thực sự tạo mã máy bằng bitwise AND khi đối số thứ hai là lũy thừa đã biết trước của hai.
Có một thế giới thực sử dụng trong câu hỏi của tôi ở đây -
Chỉ trả lời thông báo WM_KEYDOWN đầu tiên?
Khi tiêu thụ một thông điệp WM_KEYDOWN trong windows C api bit 30 chỉ định trạng thái khóa trước đó. Giá trị là 1 nếu khóa bị tắt trước khi tin nhắn được gửi hoặc bằng 0 nếu khóa lên
Chúng chủ yếu được sử dụng cho các hoạt động bitwise (bất ngờ). Dưới đây là một vài ví dụ thực tế được tìm thấy trong cơ sở mã PHP.
Mã hóa ký tự:
if (s <= 0 && (c & ~MBFL_WCSPLANE_MASK) == MBFL_WCSPLANE_KOI8R) {
Cấu trúc dữ liệu:
ar_flags = other->ar_flags & ~SPL_ARRAY_INT_MASK;
Trình điều khiển cơ sở dữ liệu:
dbh->transaction_flags &= ~(PDO_TRANS_ACCESS_MODE^PDO_TRANS_READONLY);
Trình biên dịch thực hiện:
opline->extended_value = (opline->extended_value & ~ZEND_FETCH_CLASS_MASK) | ZEND_FETCH_CLASS_INTERFACE;
Bất cứ khi nào tôi mới bắt đầu lập trình C, tôi đều hiểu các bảng chân lý và tất cả những thứ đó, nhưng tôi không biết cách sử dụng nó cho đến khi tôi đọc bài viết này http://www.gamedev.net/reference/articles/article1563.asp (đưa ra ví dụ thực tế)
x == 1
và y == 2
, sau đó x || y
ước lượng thành 1 và x | y
ước tính thành 0. Tôi cũng không thấy tại sao lại x^true
vượt trội hơn !x
bất kỳ cách nào. Nó gõ nhiều hơn, ít thành ngữ hơn và nếu x
không xảy ra bool
thì nó không đáng tin.
x^true
vượt trội hơn !x
là some->complicated().member->lookup ^= true;
Không có phiên bản gán ghép của các toán tử đơn nguyên.
Giải pháp tuyến tính Tower Of Hà Nội sử dụng các hoạt động bitwise để giải quyết vấn đề.
public static void linear(char start, char temp, char end, int discs)
{
int from,to;
for (int i = 1; i < (1 << discs); i++) {
from = (i & i-1) % 3;
to = ((i | i-1) + 1) % 3;
System.out.println(from+" => "+to);
}
}
Giải thích cho giải pháp này có thể được tìm thấy ở đây