Bitwise operations are one of the basic operations in C/C ++. Even so, it is a strange operation for most programmers-most programmers seldom use bitwise operations. This article first briefly introduces the basic bitwise operators and their usage (when to use them), and then introduces several typical applications of bitwise operators:
(1) Three TypesInstances that exchange two integers without temporary variablesAnd analyzes the advantages and disadvantages of each instance
(2)Hexadecimal conversionThe bitwise operation is used to output the decimal number in binary and hexadecimal notation and obtain a general program for outputting the decimal number in the n hexadecimal notation of 2.
(3) Given the bitwise operation implementationCalculates the number of 1 in the binary representation of an integer..
Unveil bit operations
All data is stored in binary format at the bottom of the computer. A data can be seen as an ordered bit set. Each digit has only two States: 0 or 1. Bitwise operations allow programmers to operate on a specific location of data, such as setting a value to 1 (or 0) and querying a certain State (1, or 0 ). Bitwise operations are composed of bitwise operators and operands. Different bitwise operators define different bitwise operations, the following table describes each bitwise operator and its corresponding bitwise operations and functions:
Bitwise operators |
Bitwise operations |
Usage |
Function Description |
~ |
Non-bitwise |
~ Expr |
Flip each bit of expr: 1 to 0, 0 to 1 |
< |
Move left |
Expr <n |
Moving the expr to the left is discarded, and the right side is filled with 0. Therefore, the left side is multiplied by 2n. |
> |
Right Shift |
Expr> N |
Remove n digits from the expr to the right. If expr is of the unsigned type, add 0 to the left. Otherwise, insert a copy of the symbol bit on the left or 0 (depending on the specific implementation ). |
& |
Bitwise AND |
Expr1 & expr2 |
If both expr1 and expr2 contain 1, the result is 1; otherwise, the result is 0. |
| |
By bit or |
Expr1 | expr2 |
Where each bit is located, if both expr1 and expr2 contain 0, the result is 0; otherwise, the result is 1. |
^ |
Bitwise OR |
Expr1 ^ expr2 |
If expr1 and expr2 are different in the location of each bit, the result is 1; otherwise, the result is 0. |
In addition to the basic bitwise operators above, there are also composite symbols such as & =, ^ =, |=, <<=, >>=, which are bitwise AND assign values respectively, bitwise OR assignment, bitwise OR assignment, left shift assignment, and right shift assignment. Next we will introduce how to implement bit operations:
1. Set the Nth (N starting from 0) bit of expr to 1: expr | = (1 <n );
2. Set the Nth (N starts from 0) bit of expr to 0: expr & = (~ (1 <n ));
3. Determine whether the Nth (N starting from 0) bit of expr is 1: bool B = expr & (1 <n );
4. Flip the nth digit (N starts from 0) of the expr: expr ^ = (1 <n );
Note:
1. The C standard provides bitset for a variety of bit operations. You can enter bitset in msdn to learn the relevant content. The header file must be included: # include "bitset ".
2. bitwise operations can only be used to operate on numbers of integer types, such as char, short, Int, and long (including signed and unsigned). Do not operate on floating point numbers, such as float and double! STD: the parameter of the bitset constructor is unsigned long Int. Try not to operate on negative numbers because of poor portability, different system platforms have different definitions for the right-shift operation of negative numbers (most platforms require high-end sign-up bits, and some platforms require high-end 0 ).
Bitwise operation Example 1: Exchange two integers without any intermediate Variables
This is a classic question. You can easily find multiple answers online. Here I will provide two solutions:
Solution 1: implement with arithmetic operations (an imperfect solution)
The solution has a simple idea and a short implementation code, as shown below:
View plainprint?
- Template <class T>
- Void myswap_1 (T & A, T & B)
- {
- A = A + B;
- B = A-B;
- A = A-B;
- }
Simple, but I want to give it a simple explanation: the first sentence a = a + B is to use a to save the sum of the original A and original B; the second sentence B = A-B; so that the original a value is saved to B; the last sentence a = A-B; so that the original B value is saved to.
We can say that this method is not so perfect because the result overflow may occur in arithmetic operations. If a and B are very large, then the first sentence a = a + B will cause result overflow. For example, if the original A = 2147483647, B = 2, then a + B is 2147483649, this number is greater than the maximum unsigned integer 2147483648, so overflow occurs. The result saved in a is actually:-2147483647,However, it is surprising that, although the result of the first program is-2147483647, the result of the last two statements is correct, that is, the original A and B values can be exchanged, that is: only the results in the first sentence are incorrect, but the final results are correct. I am confused about this. I have not figured out the reason yet. I would like to ask you again!
Finally, let's talk about the advantages of this method over solution 2: This method can be used to exchange two non-INTEGER (floating point number), while solution 2 is based on bitwise operations, the bitwise operation cannot be directly used for floating point numbers. Therefore, solution 2 cannot be used to exchange two floating point numbers!
Solution 2: implement with bitwise operations (better solution)
The solution code is similar to solution 1, and the idea is not difficult. Let's look at the code first, and then let's look at my lengthy analysis:
View plainprint?
- Template <class T>
- Void myswap_2 (T & A, T & B)
- {
- A = a ^ B;
- B = B ^;
- A = a ^ B;
- }
This exchange function is no stranger to programmers, but I believe that some of these programmers only remember to write the code and do not know why the three statements of code are written, as a matter of fact, at the beginning, I thought it would be better to simply remember the three lines of code that made me spend time understanding analysis. As a matter of fact, when I wrote this article today, I was willing to consume some brain cells to understand it. Next I tried to elaborate on the above three statements. For convenience, let's assume that the data type is Char, and a = 5, B = 3; in the memory, A and B are stored as follows:
Next, we will analyze each sentence in detail:
First, let's look at the first sentence: A = a ^ B; after this statement is executedA stores the difference between A and B.That is to say, if A is different from B, the location of a is 1, so a becomes like this in the memory, it indicates that the second and second bits of A and B are different:
Next let's look at the second sentence: B = B ^ A; it means that there is a difference between BFlipIn this way, the value saved in B is actually equal to the value in the original A. Remember that after the second statement is executed, a still saves the difference information of the original A and B, B becomes the original!
Finally, let's look at the third sentence: A = a ^ B; because the XOR operation satisfies the exchange law, this sentence is equivalent to: a = B ^; remember that the original a value has been saved in B on the right of the value assignment, while the original A and B differences are saved in, therefore, the final function of this sentence is to flip (change to B) the bitwise of the original A and assign it to a, so that the original B value is saved in.
Conclusion: In the above three sentences, the first sentence is to record the differences, and the second and second sentences are flipped. Finally, the values of the two variables are exchanged without any intermediate variables.
Analysis: bitwise operations do not consider the carry issue, so there is no result overflow problem! However, since it is not possible to perform direct bitwise operations on floating point numbers, this method cannot be used to exchange two floating point numbers! Of course, the original question is to exchange two integers.
Note: There are other methods to achieve two data exchanges, such as using memory copy! Because it does not belong to the bit operation category, we will not go into details here.
Bitwise Application Example 2: hexadecimal conversion
Requirements: the decimal integers are output in binary and hexadecimal notation respectively.
Two methods are provided to achieve binary output:
Method 1: Since integers are stored in binary format in a computer, we only need to print each bit in order. If a bit is 1, the character '1' is printed ', otherwise, the character '0' is printed '. The code I provided is as follows:
View plainprint?
- Voidprintbinary (INT num)
- {
- For (INT I = 0; I <32; I ++)
- {
- Cout <(Num> (31-i) & 1 );
- // Cout <(Num & (1 <(31-i) = 0? 0: 1 );
- }
- }
The commented-out cout has the same function as the uncommented cout! The idea of this function is very simple, that is, to print every bit from the height to the bottom. The code above is a bit bad, that is, the statement is too complicated. A cout statement does too many things. If it affects your understanding, you can add several temporary variables, split the statement into multiple simple statements. I write this mainly for space reasons, so the program section is too space-consuming. When programming, the statement strives to be simple and clear: One Line writes only one statement, and one statement does only one thing!
Method 2: Implement bitset
Bitset is a class (not a container) provided by the standard library. It can be used to conveniently operate bits. The following is a program implemented using bitset:
View plainprint?
- Voidprintbinary (INT num)
- {
- Bitset <32> bits = bitset <32> (unsigned long) (Num ));
- For (INT I = 31; I> = 0; I --)
- {
- Cout <(BITS [I] = true? '1': '0 ');
- }
- }
Note: bitset overload multiple operators, including the subscript operator []. You can conveniently obtain a bit and check whether it is 1. For more information about bitset, refer to msdn or other materials. You only need to remember that bitset is provided by the standard library and can be used at any time. Do not forget to add the corresponding header file.
Implement hexadecimal output:
Similarly, because the data is stored in the memory in binary format, we can output integers in hexadecimal notation as follows: from left to right, a group of 4 bits each, combine them into a hexadecimal number and output them at a time. The program is as follows:
View plainprint?
- Void printhex (INT num)
- {
- For (INTI = 28; I> = 0; I-= 4)
- {
- Int temp = num> I;
- Temp = temp & 15; // 15 is the mask!
- Char ch;
- Temp> 9? (CH = 'A' + temp-10) :( CH = '0' + temp );
- Cout <ch;
- }
- }
This program is very similar to the printbinary function above. Note that I changes 4 every time. The most important thing is that the statement temp = temp & 15; because it is in hexadecimal format, so here we use 15 as the mask. I want to pave the way for printbinary. It is not difficult to understand this printhex. I will not repeat it here. Next, I will make a small extension to these two functions: Implement integer-based output in 2n (n of 2! For example, it can be in octal or 32 hexadecimal format. For ease of description, we limit 1 <= n <= 6; and use the characters '0' to '9' to indicate numbers 0 to 9. Use the characters a, B ,...... Z, a, B ,...... The value ranges from 10 to 63. The procedure is as follows:
View plainprint?
-
- Void print2powern (INT num, int N)
-
- {
-
- For (INTI = 32-n; I> = 0; I-= N)
-
- {
-
- Int temp = num> I;
-
- Temp = temp & (1 <n)-1 );
-
- Char ch;
-
- If (temp <= 9)
- {
-
- Ch = '0' + temp;
-
- }
-
- Elseif (temp <= 35)
-
- {
-
- Ch = 'A' + temp-10;
-
- }
-
- Else
-
- {
- Ch = 'A' + temp-36;
-
- }
-
- Cout <ch;
-
- }
-
- }
Note: bitwise operations can also achieve conversion in decimal to any expected hexadecimal format. This problem is quite difficult and I have not yet completed it!
Bitwise Case 3: Evaluate the number of 1 in the binary representation of an integer
Problem description:To input an integer N, you must output its binary representation of the number M of 1. For example, if n = 13, M = 3;
Analysis:There are more than one method to solve this problem, which can be implemented by scanning each bit in binary representation. The complexity of this method is O (n) n is the total number of digits in the binary representation of N. This section describes how to use bitwise operations to solve the problem and ensure that the complexity is lower than O (N). In fact, the complexity of this method is O (m ), where m is the number of 1 in the binary ID of n!
Ideas:When talking about the specific implementation, let's look at the fact that:N & (n-1) is able to flip 1 of The percentile! For example, if n = 108 and its binary value is 01101100, the result of N & (n-1) is 01101000. Therefore, as long as you constantly flip the 1 of the second bit of N, each time you flip the counter, let the counter + 1 until n is equal to 0, the counter records the 1-digit number of the second of N, the procedure is as follows:
View plainprint?
- Int count1bits (long N)
- {
- Int COUNT = 0;
- While (N)
- {
- Count ++;
- N & = (n-1 );
- }
- Return count;
- }
Bitwise operations in Computer