位運算實現加減乘除四則運算

2022-05-12 04:26:53 字數 4096 閱讀 6646

目錄

加法減法

乘法除法

計算機最基本的操作單元是位元組(byte),乙個位元組由8個位(bit)組成,乙個位只能儲存乙個0或1,其實也就是高低電平。無論多麼複雜的邏輯、龐大的資料、酷炫的介面,最終體現在計算機最底層都只是對0101的儲存和運算。

不考慮進製情況下,位的異或運算跟求'和'的結果一致:

異或 1^1=0 1^0=1 0^0=0

求和 1+1=0 1+0=1 0+0=0

位的與運算跟求'進製『的結果也是一致:

位與 1&1=1 1&0=0 0&0=0

進製 1+1=1 1+0=0 0+0=0

舉個例子:

13 + 9 = 22

我們像這樣來拆分這個運算過程:

不考慮進製,分別對各位數進行相加,結果為sum: 

個位數3加上9為2;十位數1加上0為1; 最終結果為12;

只考慮進製,結果為carry: 

3 + 9 有進製,進製的值為10;

如果步驟2所得進製結果carry不為0,對步驟1所得sum,步驟2所得carry重複步驟1、 2、3;如果carry為0則結束,最終結果為步驟1所得sum: 

這裡即是對sum = 12 和carry = 10重複以上三個步驟,(a) 不考慮進製,分別對各位數進行相加:sum = 22; (b) 只考慮進製: 上一步沒有進製,所以carry = 0; (c) 步驟2carry = 0,結束,結果為sum = 22. 

我們發現這三板斧行得通!

那我們現在還使用上面的三板斧把十進位制運算放在二進位制中看看是不是也行的通。

13的二進位制為0000 1101,9的二進位制為0000 1001:

不考慮進製,分別對各位數進行相加: 

sum = 0000 1101 + 0000 1001 = 0000 0100

考慮進製: 

有兩處進製,第0位和第3位,只考慮進製的結果為: 

carry = 0001 0010

步驟2carry == 0 ?,不為0,重複步驟1 、2 、3;為0則結束,結果為sum: 

本例中, 

(a)不考慮進製sum = 0001 0110; 

(b)只考慮進製carry = 0; 

(c)carry == 0,結束,結果為sum = 0001 0110 

轉換成十進位制剛好是22.

我們發現,適用於十進位制的三板斧同樣適用於二進位制!仔細觀察者三板斧,大家能不能發現其實第一步不考慮進製的加法其實就是異或運算;而第二部只考慮進製就是與運算並左移一位–;第三步就是重複前面兩步操作直到第二步進製結果為0。

這裡關於第三步多說一點。為什麼要迴圈步驟1、 2、 3直到步驟2所得進製carry等於0?其實這是因為有的數做加法時會出現連續進製的情況,舉例:3 + 9,我們來走一遍上述邏輯:

c++**:

//非遞迴寫法

int add(int num1, int num2)

return n1|n2;

}

//遞迴寫法

int add(int num1,int num2)

問題:負數在計算機中是怎麼表示的?

8在計算機中表示為二進位制的1000,那-8怎麼表示呢?

很容易想到,可以將乙個二進位制位(bit)專門規定為符號位,它等於0時就表示正數,等於1時就表示負數。比如,在8位機中,規定每個位元組的最高位為符號位。那麼,+8就是00001000,而-8則是10001000。這只是直觀的表示方法,其實計算機是通過2的補碼來表示負數的,那什麼是2的補碼(同補碼,英文是2』s complement,其實應該翻譯為2的補碼)呢?它是一種用二進位制表示有號數的方法,也是一種將數字的正負號變號的方式,求取步驟:

我們知道,減法與加法運算互為逆運算,當我們求a-b,其實是求[a-b]補。因為有[a-b]補=[a]補 - [b]補= [a]補+[-b]補。所以我就要先求出-b。求乙個數的負的操作是將其連符號位一起取反然後加1。於是有辦法做減法了:先把減數求負,然後做加法。

int subtraction(int a, int b) 

前邊已經講過了加法運算和減法運算的實現,而乘法運算可以轉化為加法運算,但是需要考慮符號問題。

因此乘法運算分為兩個部分。

1)計算絕對值乘積;

2)根據同正異負,確定符號位。

根據以上總結寫出**:

int multiply(int a, int b)    

// 確定乘積的符號  

if((a ^ b) < 0)

return product;

}

上面的思路在步驟上沒有問題,但是第一步對絕對值作乘積運算我們是通過不斷累加的方式來求乘積的,這在乘數比較小的情況下還是可以接受的,但在乘數比較大的時候,累加的次數也會增多,這樣的效率不是最高的。我們可以思考,如何優化求絕對值的乘積這一步。

第二種思路:二進位制乘法!

這一過程就是根據乘數的每一位為0或1時,將被乘數錯位的加在積上。時間複雜度為o(logn)。

(1) 判斷乘數是否為0,為0跳轉至步驟(4)

(2) 將乘數與1作與運算,確定末尾位為1還是為0,如果為1,則相加數為當前被乘數;如果為0,則相加數為0;將相加數加到最終結果中;

(3) 被乘數左移一位,乘數右移一位;回到步驟(1)

(4) 確定符號位,輸出結果;

int multiply(int a, int b)      

multiplicand = multiplicand << 1;// 每運算一次,被乘數要左移一位    

multiplier = multiplier >> 1;// 每運算一次,乘數要右移一位(可對照上圖理解)  

}   

//計算乘積的符號  

if((a ^ b) < 0)   

return product;

}

第二種方法要優於第一種方法。

根據乘法運算的第一種思路,很多人想到把除法運算轉換成減法運算,即不停的用除數去減被除數,直到被除數小於除數時,此時所減的次數就是我們需要的商,而此時的被除數就是餘數。這裡需要注意的是符號的確定,商的符號和乘法運算中乘積的符號確定一樣,即取決於除數和被除數,同號為證,異號為負;餘數的符號和被除數一樣。

int division(int a, int b)

if (flag)

return add(~n, 1);

return n;

}

這種方法很容易想到,演算法時間複雜度為o(n)。但是當被除數非常大,除數非常小,那就需要進行很多的減法運算。簡單地說,就是減的太慢了,得找個方法讓它加速一下。

於是採用類似二分法的思路,從除數*最大倍數開始測試,如果比被除數小,則要減去,下一回讓除數的倍數減少為上一次的一半,當倍數為1時,就把被除數中所有的除數減去,並得到商。時間複雜度優化到o(logn)。

計算機是乙個二元的世界,所有的int型資料都可以用[2^0, 2^1,…,2^31]這樣一組基來表示(int型最高31位)。不難想到用除數的2^31,2^30,…,2^2,2^1,2^0倍嘗試去減被除數,如果減得動,則把相應的倍數加到商中;如果減不動,則依次嘗試更小的倍數。這樣就可以快速逼近最終的結果。

2的i次方其實就相當於左移i位,為什麼從31位開始呢?因為int型資料最大值就是2^31啊。

參考文獻:

位運算實現加減乘除四則運算

只用邏輯運算實現加法 int add int a,int b int sumtemp a b int carry a b 1 return add 只用邏輯運算實現減法 int minus int a,int b 邏輯運算實現除法 低效 int div int a,int b return resu...

位運算實現加減乘除四則運算

目錄加法 減法 乘法 除法計算機最基本的操作單元是位元組 byte 乙個位元組由8個位 bit 組成,乙個位只能儲存乙個0或1,其實也就是高低電平。無論多麼複雜的邏輯 龐大的資料 酷炫的介面,最終體現在計算機最底層都只是對0101的儲存和運算。不考慮進製情況下,位的異或運算跟求 和 的結果一致 異或...

python 位運算實現加減乘除四則運算

寫在前面 加法在測試時遇到了乙個問題,用python在算 4 8時,會無限迴圈 我列印了每一次的sum和carry,原來是carry會越來越大 到後面會非常大 但是我試了下c 發現是可以計算的,它溢位後會變成4 0 1 加法 def add a,b 遞迴 if b 0 return a sum a ...