Binary: do not understand the source of computers, what programming do you learn

img

We all know that the origin of computers is the binary notation in mathematics. It can be said that without binary, there would be no computer system today. So what is binary? Why do computers use binary instead of decimal in our daily lives? How to manipulate binary in code? From the beginning of the column, we started from the origin of computer cognition-binary, and talked about its "mystery" in computers.

1. What is binary notation?

To give you a better understanding of binary notation, let's briefly review the history of human counting.

In primitive times, humans used roadside pebbles to count the number of sheep returning from grazing, which shows that we have a sense of counting very early. Later, the Romans used fingers as a counting tool and drew Ⅰ, Ⅱ, and Ⅲ on sheepskin to replace the number of fingers. When it means one hand, it is written in a "Ⅴ" shape, when it means two hands, it is painted in a "ⅤⅤ" shape and so on.

Around the 3rd century AD, Indian mathematicians (also known as Arabs) invented Arabic numerals. Arabic numerals are composed of 10 counting symbols from 0 to 9, and adopt the carry system , with the high digit on the left, the low digit on the right, and writing from left to right. Because Arabic numerals have simple strokes and convenient calculations, they have gradually become popular in various countries and become universal numbers in the world.

In daily life, the decimal notation that we widely use is also based on Arabic numerals. This is also the basis of decimal notation. Therefore, relative to other counting methods, decimal is the easiest to understand.

Let us observe a number: 2871.

img

Where ^ means power or power operation. The decimal digits (thousands, hundreds, tens, etc.) are all in the form of 10^n. It is important to note that any non-zero number raised to the power of 0 is 1. In this new expression, 10 is called the base of decimal notation, which is also the origin of the "ten" in decimal. I think you should understand this well, because it is consistent with our daily habits.

Understand the decimal number, we try to use a similar idea to understand the definition of binary. Let me take the binary number 110101 as an example to explain it to you. Let’s take a look first, what number does 110101 represent in decimal?

Just now we said that decimal counting uses 10 as the base, then the binary uses 2 as the base. By analogy, the binary digits are in the form of 2^n . If we need to convert this number into a decimal system that is easy for people to understand, we can calculate it like this:

img

According to this idea, we can also deduce the octal (base 8), hexadecimal (base 16) and other notation methods, which are very simple, so I won't repeat them here.

At this point, you should have understood what binary is. But theoretical knowledge of mathematics is not enough, combined with relevant code practice, I believe you will have a deeper impression.

Based on this, let's take a look at how binary and decimal numbers are converted to each other in the Java language, and verify our previous calculations. What I use here is the Java language to achieve, and other mainstream programming languages ​​are implemented in similar ways.

The implementation of this code uses Java's BigInteger class and its API functions. I have added code comments and interspersed some explanations. You should be able to understand.

First, we introduce the BigInteger package and use it to convert between binary and decimal systems with API functions of the Integer class.

import java.math.BigInteger;

public class Lesson1_1 {
    
    
  
   /**

    * @Description: 十进制转换成二进制
    * @param decimalSource
    * @return String
    */
    public static String decimalToBinary(int decimalSource) {
    
    
       BigInteger bi = new BigInteger(String.valueOf(decimalSource)); //转换成BigInteger类型,默认是十进制
       return bi.toString(2); //参数2指定的是转化成二进制
    }

    /**
    * @Description: 二进制转换成十进制
    * @param binarySource
    * @return int
    */
    public static int binaryToDecimal(String binarySource) {
    
    
       BigInteger bi = new BigInteger(binarySource, 2);  //转换为BigInteger类型,参数2指定的是二进制
       return Integer.parseInt(bi.toString());     //默认转换成十进制
    }
}

Then, we use a decimal number and a binary number to verify the correctness of the above code.

public static void main(String[] args) {
    
         

      int a = 53;
      String b = "110101";
      System.out.println(String.format("数字%d的二进制是%s", a, Lesson1_1.decimalToBinary(a))); //获取十进制数53的二进制数
      System.out.println(String.format("数字%s的十进制是%d", b, Lesson1_1.binaryToDecimal(b))); //获取二进制数110101的十进制数

   }

The result of running this code is: the binary number of the decimal number 53 is 110101, and the decimal number of the binary number 110101 is 53.

Well, you should be very clear about the concepts of decimal and binary and the mutual conversion between decimals. Since there are decimal systems and binary systems, you may have to ask, why do computers use binary systems instead of decimal systems?

2. Why do computers use binary?

I think the use of binary in computers is related to the hardware implementation of modern computer systems. The logic circuits that make up a computer system usually have only two states, that is, the switch is on and off.

We use "0" to indicate the off state and "1" to indicate the on state. Because each bit of data has only two states: off and on, even when the system is disturbed to a certain extent, it can still reliably distinguish whether the number is "0" or "1". Therefore, in the specific system implementation, binary data expression has the advantages of strong anti-interference ability and high reliability.

In contrast, if a circuit with 10 states is designed in decimal, the situation will be very complicated, and the probability of error when judging the state will be greatly increased.

In addition, binary is also very suitable for logic operations. The "true" and "false" in logic operations correspond exactly to the binary numbers "0" and "1". The addition ("or" operation), multiplication ("and" operation) and negation ("not" operation) in logic operations can all be realized by the addition, multiplication and subtraction of "0" and "1".

3. Binary bit manipulation

Knowing that modern computers are based on binary, let's take a look at the bit manipulation of binary in computer language. The bit operation here , also called bit operation , is to directly manipulate the binary bits in the memory. Common binary bit operations include shift operations to shift to the left and shift to the right, and logical operations of "or", "and" and "exclusive OR". Let's look at them one by one below.

1. shift to the left

Let's first look at the shift to the left.

The binary 110101 is shifted to the left by one bit, which is to add a 0 at the end, so 110101 becomes 1101010. Please note that this is the case where the numbers do not overflow.

The so-called digital overflow is that the number of digits in the binary number exceeds the number of digits specified by the system. The current mainstream systems all support at least 32-bit integer numbers, and 1101010 is far less than 32 bits, so it will not overflow. If the binary used for the left shift operation exceeds 32 bits, the number will overflow after the left shift, and the overflowed bits need to be removed.

img

In this example, if 1101010 is converted to decimal, it is 106. Have you found that 106 is exactly twice 53. Therefore, we can draw a conclusion: shifting the binary to the left by one bit is actually doubling the number.

Expand the binary number and move it to the left by one bit, which means that the decomposed coefficients are all increased by one bit. Compared with the original binary expansion, all are multiplied by 2, which means the final result is also multiplied by 2.

2. Shift right

Next we look at shifting to the right.

The binary 110101 is shifted to the right by one bit, which is to remove the last bit, so 110101 becomes 11010 (the first 0 can be omitted). We convert 11010 to decimal, which is 26, which is exactly the integer quotient of 53 divided by 2. So shifting the binary by one bit to the right is the operation of dividing the number by 2 and finding the integer quotient.

img

Let's take a look at how to perform shift operations with code.

import java.math.BigInteger;

public class Lesson1_2 {
    
      

   /**
    * @Description: 向左移位
    * @param num-等待移位的十进制数, m-向左移的位数
    * @return int-移位后的十进制数
    */
   public static int leftShift(int num, int m) {
    
    
      return num << m;
   }
  
   /**
    * @Description: 向右移位
    * @param num-等待移位的十进制数, m-向右移的位数
    * @return int-移位后的十进制数
    */
   public static int rightShift(int num, int m) {
    
    
      return num >>> m;
   } 
 
}

Then, we verify the results with a piece of test code.

public static void main(String[] args) {
    
         

      int num = 53;
      int m = 1;
      System.out.println(String.format("数字%d的二进制向左移%d位是%d", num, m, Lesson1_2.leftShift(num, m)));   //测试向左移位
      System.out.println(String.format("数字%d的二进制向右移%d位是%d", num, m, Lesson1_2.rightShift(num, m)));   //测试向右移位     

      System.out.println();
     
      m = 3;
      System.out.println(String.format("数字%d的二进制向左移%d位是%d", num, m, Lesson1_2.leftShift(num, m)));   //测试向左移位
      System.out.println(String.format("数字%d的二进制向右移%d位是%d", num, m, Lesson1_2.rightShift(num, m)));   //测试向右移位 

   } 

The result of this code is: the number 53 is shifted by 1 bit to the left is 106; the number 53 is shifted by 1 bit to the right is 26. The number 53 shifted to the left by 3 places is 424, and the number 53 is shifted to the right by 3 places is 6.

Let me explain. Among them, 1 shift is equivalent to multiplying or dividing by 2, and shifting 3 times is equivalent to multiplying or dividing by 8 (that is, 2 to the 3rd power). If you are careful, you may have discovered that the representations of left and right shifts in Java are not the same.

The left shift is <<, so why is the right shift >>> instead of >>?

In fact, >> is also a right shift operation. Simply put, the fundamental reason for these two expressions is that the highest bit in Java's binary value is the sign bit. Here I will give you a detailed explanation.

When the sign bit is 0, it means the value is positive; when the sign bit is 1, it means the value is negative. Let's take 32-bit Java as an example. The binary of the number 53 is 110101, and the 32nd bit from right to left is 0, which means that the number is positive, but we usually omit it.

img

What if the number is -53? Then the 32nd bit is not 0, but 1. Please note that what I listed here is the complement.

img

Then shift to the right at this time, there will be a question: for the sign bit (especially when the sign bit is 1), do we also need to shift it to the right? Therefore, two right shifts are defined in Java, logical shift right and arithmetic shift right . The logic is shifted by 1 bit to the right, and 0 is added to the left.

img

When arithmetic shifts to the right, the sign bit remains unchanged, except for the sign bit, shifts one bit to the right and complements the sign bit 1. The complement of 1 is still after the sign bit.

img

Logical right shift is represented by >>> in Java and Python languages, while arithmetic shift right is represented by >>. If you are interested, you can try your own coding to see the difference between the output of these two operators.

In C or C++ language, logical shift right and arithmetic shift right share the same operator >>. So, how does the compiler decide whether to use logical shift right or arithmetic shift right? The answer is that it depends on the type of operand. If the operand type is unsigned, then logical right shift is used; if signed, then arithmetic right shift is used. If you use arithmetic right shift for unsigned type data, or use logical right shift for signed type data, then you need to perform type conversion first.

Since the left shift does not need to consider whether the high bit is complemented by 1 or 0 (the sign bit may be 1 or 0), there is no need to distinguish between logical left shift and arithmetic left shift.

3. Bit "or"

As we just said, the binary "1" and "0" correspond to the "true" and "false" in the logic, so logical operations can be performed on bits.

The logical "or" means that as long as one of the bits participating in the operation is 1, the final result is 1, which is "true". If we align each bit of the binary 110101 and 100011 and perform a bitwise OR operation, we will get 110111.

img

4. Bit "and"

In the same way, we can also perform logical AND operations on bits. "And" means that all the bits participating in the operation must be 1, so the final result is 1 (true), otherwise it is 0 (false). If we align each bit of the binary 110101 and 100011 and perform a bitwise AND operation, we will get 100001.

img

5. Bit "exclusive OR"

Logical "exclusive OR" and "or" are different. It is exclusive, which means that if the bits involved in the operation are the same, the final result is 0 (false), otherwise it is 1 (true). Therefore, if you want to get 1, the two bits involved in the operation must be different, which is the meaning of "exclusive" here. We align each bit of the binary 110101 and 100011 and perform a bitwise "exclusive OR" operation, and the result is 10110.

img

Let me summarize that the essence of the "exclusive OR" operation is that all values ​​and themselves are all 0 after the bitwise "exclusive OR" operation. And to get 0 through the "exclusive OR" operation, you must also perform a bitwise "exclusive OR" with two identical values. This shows that the bitwise exclusive OR of two values ​​is 0, which is a necessary and sufficient condition for the two values ​​to be equal, and can be used as a condition for judging whether two variables are equal.

Next, let's learn how to implement binary logic operations in the code. The use of | in Java means bitwise "or", & means bitwise "and", and ^ means bitwise "exclusive OR".

import java.math.BigInteger;

public class Lesson1_3 {
    
      

   /**
    * @Description: 二进制按位“或”的操作
    * @param num1-第一个数字,num2-第二个数字
    * @return 二进制按位“或”的结果
    */
   public static int or(int num1, int num2) {
    
    
        
      return (num1 | num2);
     
   }  

   /**
    * @Description: 二进制按位“与”的操作
    * @param num1-第一个数字,num2-第二个数字
    * @return 二进制按位“与”的结果
    */
   public static int and(int num1, int num2) {
    
         
   
      return (num1 & num2);
     
   } 

   /**

    * @Description: 二进制按位“异或”的操作
    * @param num1-第一个数字,num2-第二个数字
    * @return 二进制按位“异或”的结果
    */

   public static int xor(int num1, int num2) {
    
         

      return (num1 ^ num2);
     
   }  


}

Similarly, we write a piece of test code to verify the above three functions.

public static void main(String[] args) {
    
    

      int a = 53;
      int b = 35;

      System.out.println(String.format("数字%d(%s)和数字%d(%s)的按位‘或’结果是%d(%s)",
            a, decimalToBinary(a), b, decimalToBinary(b), Lesson2_3.or(a, b), decimalToBinary(Lesson1_3.or(a, b)))); //获取十进制数53和35的按位“或”     

      System.out.println(String.format("数字%d(%s)和数字%d(%s)的按位‘与’结果是%d(%s)",
            a, decimalToBinary(a), b, decimalToBinary(b), Lesson2_3.and(a, b), decimalToBinary(Lesson1_3.and(a, b))));  //获取十进制数53和35的按位“与”     

      System.out.println(String.format("数字%d(%s)和数字%d(%s)的按位‘异或’结果是%d(%s)",
            a, decimalToBinary(a), a, decimalToBinary(a), Lesson2_3.xor(a, a), decimalToBinary(Lesson1_3.xor(a, a))));  //获取十进制数53和35的按位“异或”     

   } 

The result of this code is: the bitwise OR of the number 53 (110101) and the number 35 (100011), and the result is 55 (110111), and the bitwise AND result of the number 53 (110101) and the number 35 (100011) It is 33 (100001), the bitwise XOR of the number 53 (110101) and the number 53 (110101) results in 0 (0).

4. Summary

Today we talked about binary, you may ask: What is the use of learning binary? In ordinary programming, we don’t seem to use relevant knowledge? Indeed, the current high-level language can help us convert human thinking logic into a machine language using 0 and 1, so we don't have to worry about it anymore. However, binary is the cornerstone of modern computer systems, and you must have a good understanding of these basic concepts and operations.

Binary runs through many common concepts and ideas, such as logical judgment, dichotomy, binary tree, and so on. The true and false value in logical judgment is represented by binary 1 and 0; both dichotomy and binary tree divide the problem to be dealt with in two, which can also be represented by binary 1 and 0. Therefore, by understanding the binary system, you can more easily understand the data structures and algorithms of many computers, and lay the foundation for our subsequent learning.

Questions

If we don't use the BigInteger class that comes with the Java language, what other method do we have to convert from decimal to binary? (Hint: You can use binary shift and bitwise logic operations to achieve.)

Guess you like

Origin blog.csdn.net/qq_33254766/article/details/108702875