不使用 +-×÷ 運算符來實現 加減乘除 四項運算
最近,在看《劍指Offer——名企面試官精講典型編程題》一書,當看到“面試題47:不用加減乘除做加法”時,經過一分鐘左右思考后,得出了思路,跟書上一對照,基本一致,呵呵O(∩_∩)O~。于是,隨即又開始思考:加法是實現了,那么減法、乘法還有除法又該怎么實現呢?一番思考與分析后,得出算法,寫出代碼,測試通過,Happy!!\(^o^)/~
接下來,就將我的算法與代碼展示如下,還請有更好算法的牛人不吝賜教!!
【1. 加法】
因本人的算法與《劍指Offer——名企面試官精講典型編程題》中一致,就不做贅述,而只貼出代碼。
代碼1.1 加法:計算a+b













【2. 減法】
方案一:通過Add函數的實現
按常規思路,根據加減運算的互反性(即,減去一個數等于加上這個數的相反數),然后利用前面已實現的Add函數來進行實現。
按這個思路,我們首先要做到對一個整數取相反數,不能使用運算符“-”,那么就只能根據C++上兩個互為相反數的int型數據的二進制結構關系——整數的相反數等于該數按位取反再加1,來設計如下的函數了。
代碼2.1 取整數n的相反數




然后就可以通過Add函數來實現減法了。
代碼2.2 減法一:計算a-b




畢竟是作為對思維開放的一個鍛煉,所以對于直接的減法算法的思考還是值得的。于是就有了下面的方案二。
方案二:直接通過位操作實現減法
算法是得通過二進制位計算來實現的,所以在分析減法時從二進制減法計算的角度去考慮將更合適。那么,兩個二進制形式整數的減法操作又是怎樣進行的呢?
- 首先,如果減數為0,則被減數即為減法的結果,運算結束。
但如果減數不為0,我們可以先把被減數和減數上同為1的位從兩個數上去除。至于如何分離出值同為1的位,則可以通過求位與操作來做到;而把這些1分別中被減數和減數中去除,則可以通過按位異或來的操作來實現。 - 經步驟1處理后,被減數和減數在對應的位上,將或者通為0,或者分別為0和1,卻不會同為1。此時:
如果對應位被減數=1,而減數=0,則所得結果對應位也為1;
如果對應位被減數=0,而減數=1,則所得結果對應位還是1,但此時須向前一位借1;
即,通過被減數與減數作位異或的操作得到臨時結果,和通過對減數左移一位得到需從臨時結果中減去的借數。 - 于是,經過步驟2后,原來的減法變成了要求:臨時結果 - 借數
很明顯,只要以臨時結果為被減數,借數為減數,重復步驟1~3即可。
上述步驟中,如果被減數或減數為負數,由負數的二進制位結構,可以保證上述步驟的處理仍適用,證明過程就請恕我在這里略去了。具體的實現代碼如下。
代碼2.3 減法二:計算a-b













※注:上述加法和減法中,按代碼安全性,其實還應考慮計算后數據溢出的情況,這里我偷了下懶,省去了。不過下面的乘除法,我會提供包含了異常處理的代碼。異常處理的方式,我采用了throw拋出的方式。
【3. 乘法】
為了方便對數據溢出的統一處理,在進行計算前,我先保存了被乘數與乘數的符號信息,并當被乘數或乘數為負時,利用上面的OppositeNumber函數,統一的轉換為正整數(或0),然后再來進行乘法的運算。為了能同時適應32位和64位的整形數,在取符號信息與設置溢出判斷位時,使用了以下的輔助宏和函數。
代碼3.1 輔助宏與輔助函數





















乘法的算法。考慮到:
- 整數n乘以2k == n << k
- C++中的任何一個非負int型數據都可以表示為如下的形式:
k0×20+k1×21+...+km×2m
的形式。(其中:ki∈{0, 1}, i∈{0, 1, ... , m}, 32位int型m = 30, 64位int型m = 62)
于是,就可以利用乘法分配率,通過循環累加,進行乘法的運算了。參考代碼3.2。
代碼3.2 乘法:計算a×b
































【4. 除法】
整數的除法,不同于乘法,除法所得的商的絕對值必然不大于被除數的絕對值,而所得余數的絕對值則必然小于除數的絕對值。所以,在設計除法函數的時候,無需考慮數據溢出的問題。但對于除法,卻也有它自己的禁忌——除數不能為“0”。
為了處理的方便,準備工作同乘法一樣,記錄下被除數與除數的符號狀態(比便在計算出結果后進行符號的調整),并當被除數或除數為負時,通過函數OppositeNumber將其轉換為相反數。于是,接下來,我就只需考慮“非負整數(>=0)÷正整數(>0)”的情況了。對這種情況,計算過程如下:
- 預備工作:置商為0;
- 判斷“被除數>=除數 ”是否成立:
成立,繼續步驟3;
不成立,被除數的值賦給余數,計算結束。 - 備份除數,并設置商分子(一個臨時變量,最終需加到商上面,故暫且如此命名)為1;
對商分子和除數同步向左移位,直到繼續移位將大于被除數時為止; - 從被除數上減去除數,并將商加上商分子。
- 通過備份的除數值還原除數,跳轉到步驟2繼續執行。
對應的代碼參加代碼4.1。
代碼4.1 除法:計算a÷b





















































※注:函數的返回值即為所求的商;
參數pRem為余數的傳出參數,其默認值NULL,表示當前無需關注余數。
posted on 2012-03-30 01:30 青碧竹 閱讀(4996) 評論(7) 編輯 收藏 引用 所屬分類: 算法相關