This article reprinted address : http://www.cnblogs.com/zuoxiaolong/p/computer10.html

2.5 We focus on the addition and subtraction of binary integers, this time we continue to introduce multiplication and divide operations.

Introduction

Operation is always an important part of the program operation, and in the binary operation process, the addition operation is the heavy weight, it basically lays the foundation of binary operation. Because both subtraction and multiplication can be replaced by addition operations, only division cannot be substituted by addition.

Understanding the laws of computer operations can help us understand a lot of things that are not understood in the code of the program. For example, the overflow problem mentioned in the previous chapter, after understanding the principle of addition operation, I believe that the apes can easily know why some operations can get unexpected results.

It is also important to mention that different processors may have subtle differences in how they are operated, and therefore cannot be generalize. So most of the time we will try to discuss the abstract mathematical characteristics of operations, abstract things are always reliable, this feature for cross-platform provides the basis, but not always so, after all, LZ only heard the floating-point arithmetic standard, have not heard of the integer arithmetic standard, do not know what is LZ ignorant, There is no such thing.

Because of this, we understand that the abstraction of these operations can help us understand what the program code level does not understand.

unsigned multiplication

Unsigned multiplication is similar to addition, its operation is relatively simple, but it can also produce overflow. For unsigned numbers of two w bits, their product range is between 0 and (2w-1) 2, so it may take 2w binary to represent them. Therefore, due to the limit of the number of bits, assuming that the true product of the two W-bit unsigned number is pro, according to the truncated rule, the actual product is Pro MoD 2w.

Complement multiplication

Like addition arithmetic, the complement multiplication is also based on the unsigned, so we can easily get, for two W-bit of the complement number, assuming that their true product is pro, then the actual product is U2TW (Pro mod 2w).

We have a hypothesis that the lower w bit of the product of the W bit is the same as the low w bit of the product of the unsigned number. This means that the computer can use a single instruction to perform unsigned and complementary multiplication operations.

The proof of this process is given in the book, so let's take a look at the relationship between unsigned and complementary codes, where x ' and Y ' represent the complement codes for x and Y, respectively.

The main technique used here is 2w mod 2w = 0.

Optimization of multiplication operation

Based on the multiplication we learned in elementary school, we know that assuming that the two W bits are multiplied by the binary number, we need to do the W and the operation, and then perform the w-1 addition operation to get the result. It is not difficult to see that the time period of multiplication is very long. So the computer industry gurus come up with a way to optimize the efficiency of multiplication, using shifts and additions instead of multiplication.

The premise of the above optimization is that for a W-bit binary number, it is the product of 2k, equivalent to this binary number left shift K-bit, in the low-complement K 0. This is demonstrated in the book by the following procedure.

This process mainly uses the formula of unsigned coding, the ape friends should not be ugly to understand.

With the above foundation, we can use SHIFT and addition to optimize the multiplication. For any integer y, it always uses a binary sequence representation (assuming no more than the representation range of the binary), so we can represent the binary sequence of the product of x and y as follows (this formula is not shown in the book).

X * y = x * (yw-12w-1 + ... + y020) = (x << w-1) * yw-1 +....+ (x << 0) * y0

For example, for X * 17, we can calculate X * + x = (x << 4) + x, so we just need to shift the addition one at a time to get this multiplication done. For x * 14, you can calculate X * 8 + x * 4 + x * 2 = (x << 3) + (x << 2) + (x << 1), Faster way we can calculate, X * 16-x * 2 = ( X << 4)-(x << 1).

The last point to mention here is that the speed of addition, subtraction, and shift is not always faster than multiplication, so it is up to the speed of the two to optimize.

Unsigned division

Division is different from multiplication, it does not satisfy the assignment law of addition, that is, set y = m + N, x/y! = x/m + x/n. And unfortunately, it can sometimes be slower than multiplication, but we can only optimize for division that can be represented as 2k, convert to arithmetic right shift or logical right SHIFT K-bit (the unsigned number is the logical right SHIFT, and the logical right shift is the same as the arithmetic right shift effect).

Because it's division, we're going to deal with rounding issues. Here the value of └x/y┘ is defined as a ', X/y is a, then for a ', it is the only integer that satisfies a ' =< a < a ' +1.

For example, the value of └2.1┘ is 2, and for └-2.1┘ is-3, if itself is an integer, then equals itself.

The proof that the unsigned number is divided by the 2k equivalent less right operand shift K-bit (W > K >= 0) is relatively complex, and the LZ gives a relatively simple way of proving, not using the proof of the book. If you look at the LZ's proof can not understand, but also to reference the way the book.

We assume that for a W-bit unsigned number, it is assumed that its bit is represented as [xw-1....x0], then x = Xw-12w-1 + .... + x020. Therefore, the following results are available.

x/2k = xw-12w-1-k + ... + xk20 + xk-12-1 +...+ x02-k = b2uw-k ([xw-1....xK]) + xk-12-1 +...+ x02-k

Because xk-12-1 +...+ x02-k <= 2-1 + .... 2-k = (1) k) < 1 (here is a critical step in the proof, assuming that all bits are geometric series, then the summation formula can be used), so there is └xk-12-1 +...+ x02-k┘= 0.

So we can get └x/2k┘=└b2uw-k ([xw-1....xk]) ┘+└xk-12-1 +...+ x02-k┘=└b2uw-k ([xw-1....xk]) ┘= b2uw-k ([xw-1....xk]) = x >&G T K.

More intuitively, we can use the program to verify this result, looking at the Java code below.

Public classMain { Public Static voidMain (string[] args) {intA = 17; intb = 8; intc =b; System.out.println ("A:" +integer.tobinarystring (a)); System.out.println ("B:" +integer.tobinarystring (b)); System.out.println ("C:" +integer.tobinarystring (c)); System.out.println ("A >> 3:" + integer.tobinarystring (a >> 3)); }}

The result of this procedure is as follows, you can see that A/b result is a right shift 3-bit results, that is, the result equals a >> 3.

Complement Division

Since our program is using positive numbers just now, there is no unsigned number in Java, but we can simulate the effect of unsigned numbers. It can also be argued that complement division in the case of a positive divisor is the same effect as unsigned encoding (we do not consider the divisor is negative, because the dividend and the divisor of the sign bit can cancel each other, the following is also the same), but when the divisor is a negative number is different. Before we introduce the complement division, let's take a look at the result when a is negative, that is, the complement code.

We will change the procedure just a little bit, as follows.

Public classMain { Public Static voidMain (string[] args) {intA =-17; intb = 8; intc =b; System.out.println ("A:" +integer.tobinarystring (a)); System.out.println ("B:" +integer.tobinarystring (b)); System.out.println ("C:" +integer.tobinarystring (c)); System.out.println ("A >> 3:" + integer.tobinarystring (a >> 3)); System.out.println ("C:" +c); System.out.println ("A >> 3:" + (a >> 3)); }}

It gets the result as follows, a little unexpected.

This time, for ease of viewing, we printed the integer values of C and a >> 3, and found that the result of the shift operation was-3, and the A/b result was-2. We can see that the result of our A/b is what we expect, but the result of the shift seems to be a problem when rounding.

In fact, the reason for this problem is very simple, the complement code is similar to unsigned encoding, for bit representations have └x/2k┘= b2tw-k ([xw-1....xk]) = x >> K. However, due to a negative number, rounding down is taken. It has been mentioned above that the value of └-2.1┘ is-3.

So we get a conclusion that when there is a rounding occurrence, a negative shift to the right of K is not equivalent to dividing it by 2k.

The remedy of division

Since a negative right shift k bit is not equivalent to dividing it by 2k in the rounding. So in order to use this optimization, the great gods of the computer community naturally have to find a way to solve this problem. So they came up with a way to "offset" the value (having to admire the great gods).

First we define a ┌x/y┐ value of a ', X/y is a, then for a ', it is the only integer that satisfies a '-1 < a <= a '.

On the basis of the above definition, the meaning of "bias" is that we have ┌x/y┐=└ (x+y-1)/y┘. The proof of this process is not difficult to understand, we assume x = KY + R (we consider r > 0, there will be rounding occurring), then there is.

└ (x+y-1)/y┘=└ (ky+r+y-1)/y┘= K +└ (r+y-1)/y┘= K + 1

You can see that after doing this processing, that is, the X plus y-1 offset, at this time in the rounding, the result will be added 1 on the original basis. This is exactly what "biasing" means, and it will round out the rounding "offset".

Here we will complement the division of the program in this way to fix it, see if this is the result, as follows.

Public classMain { Public Static voidMain (string[] args) {intA =-17; intb = 8; intc =b; System.out.println ("A:" +integer.tobinarystring (a)); System.out.println ("B:" +integer.tobinarystring (b)); System.out.println ("C:" +integer.tobinarystring (c)); System.out.println ("(a+b-1) >> 3:" + integer.tobinarystring ((a+b-1) >> 3)); System.out.println ("C:" +c); System.out.println ("(a+b-1) >> 3:" + ((a+b-1) >> 3)); }}

Here we will a "bias", that is, add b-1 offset, we look at the results.

It can be seen that, after biasing, the result of the shift operation will be what we expect when the negative result is placed, so that we can use this technique to optimize it.

Article Summary

Here, the operation of the binary integer is introduced, this chapter is more difficult, so the apes may have to take some effort. In the next chapter we will enter the floating-point world and look forward to it together.

In-depth understanding of computer systems (2.6)---Multiplication and division operations of binary integers (important) "high degree of difficulty"