• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            C++之竹

            無論是太陽下,還是風雨中,都要成長!

            常用鏈接

            統計

            最新評論

            不使用 +-×÷ 運算符來實現 加減乘除 四項運算

            最近,在看《劍指Offer——名企面試官精講典型編程題》一書,當看到“面試題47:不用加減乘除做加法”時,經過一分鐘左右思考后,得出了思路,跟書上一對照,基本一致,呵呵O(∩_∩)O~。于是,隨即又開始思考:加法是實現了,那么減法、乘法還有除法又該怎么實現呢?一番思考與分析后,得出算法,寫出代碼,測試通過,Happy!!\(^o^)/~

             接下來,就將我的算法與代碼展示如下,還請有更好算法的牛人不吝賜教!!

             

            【1. 加法】

             因本人的算法與《劍指Offer——名企面試官精講典型編程題》中一致,就不做贅述,而只貼出代碼。

            代碼1.1 加法:計算a+b

            int Add(int a, int b)
            {
                
            int sum = a ^ b;
                
            int carry = a & b;
                
            while (carry != 0{
                    a 
            = sum;
                    b 
            = carry << 1;
                    sum 
            = a ^ b;
                    carry 
            = a & b;
                }


                
            return sum;
            }

            【2. 減法】

            方案一:通過Add函數的實現

            按常規思路,根據加減運算的互反性(即,減去一個數等于加上這個數的相反數),然后利用前面已實現的Add函數來進行實現。

            按這個思路,我們首先要做到對一個整數取相反數,不能使用運算符“-”,那么就只能根據C++上兩個互為相反數的int型數據的二進制結構關系——整數的相反數等于該數按位取反再加1,來設計如下的函數了。

            代碼2.1 取整數n的相反數

            int OppositeNumber(int n)
            {
                
            return Add(~n, 1);
            }

            然后就可以通過Add函數來實現減法了。

            代碼2.2 減法一:計算a-b

            int Subtract(int a, int b)
            {
                
            return Add(a, OppositeNumber(b));
            }

            畢竟是作為對思維開放的一個鍛煉,所以對于直接的減法算法的思考還是值得的。于是就有了下面的方案二。

            方案二:直接通過位操作實現減法

            算法是得通過二進制位計算來實現的,所以在分析減法時從二進制減法計算的角度去考慮將更合適。那么,兩個二進制形式整數的減法操作又是怎樣進行的呢?

            1. 首先,如果減數為0,則被減數即為減法的結果,運算結束。
              但如果減數不為0,我們可以先把被減數和減數上同為1的位從兩個數上去除。至于如何分離出值同為1的位,則可以通過求位與操作來做到;而把這些1分別中被減數和減數中去除,則可以通過按位異或來的操作來實現。
            2. 經步驟1處理后,被減數和減數在對應的位上,將或者通為0,或者分別為0和1,卻不會同為1。此時:
              如果對應位被減數=1,而減數=0,則所得結果對應位也為1;
              如果對應位被減數=0,而減數=1,則所得結果對應位還是1,但此時須向前一位借1;
              即,通過被減數與減數作位異或的操作得到臨時結果,和通過對減數左移一位得到需從臨時結果中減去的借數。
            3. 于是,經過步驟2后,原來的減法變成了要求:臨時結果 - 借數
              很明顯,只要以臨時結果為被減數,借數為減數,重復步驟1~3即可。

            上述步驟中,如果被減數或減數為負數,由負數的二進制位結構,可以保證上述步驟的處理仍適用,證明過程就請恕我在這里略去了。具體的實現代碼如下。

            代碼2.3 減法二:計算a-b

            int Subtract(int a, int b)
            {
                
            while (b != 0
                
            {
                    
            int sameBits = a & b;
                    a 
            ^= sameBits;
                    b 
            ^= sameBits;
                    a 
            |= b;
                    b 
            <<= 1;
                }


                
            return a;
            }

            ※注:上述加法和減法中,按代碼安全性,其實還應考慮計算后數據溢出的情況,這里我偷了下懶,省去了。不過下面的乘除法,我會提供包含了異常處理的代碼。異常處理的方式,我采用了throw拋出的方式。

             【3. 乘法】

            為了方便對數據溢出的統一處理,在進行計算前,我先保存了被乘數與乘數的符號信息,并當被乘數或乘數為負時,利用上面的OppositeNumber函數,統一的轉換為正整數(或0),然后再來進行乘法的運算。為了能同時適應32位和64位的整形數,在取符號信息與設置溢出判斷位時,使用了以下的輔助宏和函數。

            代碼3.1 輔助宏與輔助函數

            #define BITS_OF_ONE_BYTE     8
            #define SIGN_BIT_FLAG_FOR_BYTE   0x80     // for signed byte/char

            int SignBitFlagOfInt()
            {
                
            static int signBitFlag = 0;

                
            static bool bIs1stCalled = true;
                
            if (bIs1stCalled)
                
            {
                    
            int temp = SIGN_BIT_FLAG_FOR_BYTE;
                    
            while (temp != 0
                    
            {
                        signBitFlag 
            = temp;
                        temp 
            <<= BITS_OF_ONE_BYTE;
                    }

                    bIs1stCalled 
            = false;
                }


                
            return signBitFlag;
            }

            乘法的算法。考慮到:

            1. 整數n乘以2k == n << k
            2. 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

            int Multiply(int a, int b)
            {
                
            int signStatA = a & SignBitFlagOfInt();
                
            if (signStatA != 0)
                
            {
                    a 
            = OppositeNumber(a);
                }


                
            int signStatB = b & SignBitFlagOfInt();
                
            if (signStatB != 0)
                
            {
                    b 
            = OppositeNumber(b);
                }


                
            int overFlowFlag = SignBitFlagOfInt(); // used for checking if the signed int data overflowed.
                int product = 0// the result of |a| * |b|
                for (int curBitPos = 1; curBitPos != 0; curBitPos <<= 1)
                
            {
                    
            if ((b & curBitPos) != 0)
                    
            {
                        product 
            = Add(product, a);
                        
            if (((a & overFlowFlag) != 0
                            
            || ((product & overFlowFlag) != 0))
                        
            {
                            
            throw std::exception("The result data war overflowed.");
                        }

                    }

                    a 
            <<= 1;
                }


                
            return ((signStatA ^ signStatB) == 0? product : OppositeNumber(product);
            }

            【4. 除法】

            整數的除法,不同于乘法,除法所得的商的絕對值必然不大于被除數的絕對值,而所得余數的絕對值則必然小于除數的絕對值。所以,在設計除法函數的時候,無需考慮數據溢出的問題。但對于除法,卻也有它自己的禁忌——除數不能為“0”。

            為了處理的方便,準備工作同乘法一樣,記錄下被除數與除數的符號狀態(比便在計算出結果后進行符號的調整),并當被除數或除數為負時,通過函數OppositeNumber將其轉換為相反數。于是,接下來,我就只需考慮“非負整數(>=0)÷正整數(>0)”的情況了。對這種情況,計算過程如下:

            1. 預備工作:置商為0;
            2. 判斷“被除數>=除數 ”是否成立:
              成立,繼續步驟3;
              不成立,被除數的值賦給余數,計算結束。
            3. 備份除數,并設置商分子(一個臨時變量,最終需加到商上面,故暫且如此命名)為1;
              對商分子和除數同步向左移位,直到繼續移位將大于被除數時為止;
            4. 從被除數上減去除數,并將商加上商分子。
            5. 通過備份的除數值還原除數,跳轉到步驟2繼續執行。

            對應的代碼參加代碼4.1。

            代碼4.1 除法:計算a÷b 

            int Divide(int a, int b, int * pRem /*= NULL*/)
            {
                
            if (b == 0)
                
            {
                    
            throw std::exception("Invalid divisor!! (The divisor can't be 0!)");
                }


                
            int signStatA = a & SignBitFlagOfInt();
                
            if (signStatA != 0)
                
            {
                    a 
            = OppositeNumber(a);
                }


                
            int signStatB = b & SignBitFlagOfInt();
                
            if (signStatB != 0)
                
            {
                    b 
            = OppositeNumber(b);
                }


                
            int quotient = 0;
                
            int backupB = b;
                
            while (a >= b) 
                
            {
                    
            int tempB = b << 1;
                    
            int tempQ = 1;
                    
            while ((tempB <= a) && ((tempB & SignBitFlagOfInt()) == 0))
                    
            {
                        b 
            = tempB;
                        tempQ 
            <<= 1;
                        tempB 
            <<= 1;
                    }


                    a 
            = Subtract(a, b);
                    quotient 
            |= tempQ;
                    b 
            = backupB;
                }


                
            if (pRem != NULL)
                
            {
                    
            *pRem = a;
                }


                
            if ((signStatA != 0&& (a != 0))
                
            {
                    quotient 
            = Add(quotient, 1);
                    
            if (pRem != NULL)
                    
            {
                        
            *pRem = Subtract(b, *pRem);
                    }

                }


                
            return ((signStatA ^ signStatB) == 0? quotient : OppositeNumber(quotient);
            }

            ※注:函數的返回值即為所求的商;
                   參數pRem為余數的傳出參數,其默認值NULL,表示當前無需關注余數。

             

            posted on 2012-03-30 01:30 青碧竹 閱讀(4985) 評論(7)  編輯 收藏 引用 所屬分類: 算法相關

            評論

            # re: 不使用 +-×÷ 運算符來實現 加減乘除 四項運算 2012-03-30 13:02 遠行

            搜了下那本書,發現好像沒出版,lz有那本書的文檔能不能發一份啊,
            愛好書之情難自抑啊。。。  回復  更多評論   

            # re: 不使用 +-×÷ 運算符來實現 加減乘除 四項運算 2012-03-30 14:40 FiO

            @遠行
            http://book.douban.com/subject/6966465/  回復  更多評論   

            # re: 不使用 +-×÷ 運算符來實現 加減乘除 四項運算 2012-03-30 18:26 遠行

            沒錢買書,只看電子版。。。@FiO
              回復  更多評論   

            # re: 不使用 +-×÷ 運算符來實現 加減乘除 四項運算 2012-03-30 19:24 flyinghearts


            嚴格的講,這些方法只能用于無符號數。對有符號數這樣的做法是不通用的。 google 一補數。

              回復  更多評論   

            # re: 不使用 +-×÷ 運算符來實現 加減乘除 四項運算 2012-03-30 19:49 青碧竹

            @flyinghearts
            在目前計算機內負整數通過對其絕對值進行求反+1來存儲的前提下,這些算法完全適用于有符號整數。  回復  更多評論   

            # re: 不使用 +-×÷ 運算符來實現 加減乘除 四項運算 2012-03-31 13:15 SwordLance

            這類問題,只要知道CPU的運算器是怎么做的,用程序模擬即可。  回復  更多評論   

            # re: 不使用 +-×÷ 運算符來實現 加減乘除 四項運算 2012-04-01 00:13 青碧竹

            @SwordLance
            是的。  回復  更多評論   

            99精品久久久久久久婷婷| 久久ZYZ资源站无码中文动漫| 久久免费精品视频| 久久精品无码专区免费| 色天使久久综合网天天| 亚洲精品乱码久久久久久自慰| 精品国产福利久久久| 人妻无码久久精品| 久久发布国产伦子伦精品| 久久国产精品波多野结衣AV | 久久久久久久97| 老司机国内精品久久久久| 久久国产欧美日韩精品免费| 久久亚洲中文字幕精品有坂深雪| 国产99久久九九精品无码| 国内精品人妻无码久久久影院导航| 亚洲国产精久久久久久久| 久久伊人五月丁香狠狠色| 97超级碰碰碰碰久久久久| 亚洲AV无码久久精品成人| 久久久久女教师免费一区| 国产精品久久久久久福利漫画| 亚洲精品tv久久久久久久久| 久久久人妻精品无码一区 | 日韩va亚洲va欧美va久久| 国产精品久久成人影院| 久久婷婷五月综合97色| 久久性生大片免费观看性| 久久免费线看线看| 久久精品aⅴ无码中文字字幕不卡 久久精品aⅴ无码中文字字幕重口 | 久久久国产打桩机| 久久精品国产只有精品66| 四虎国产精品免费久久久| 久久w5ww成w人免费| 精品久久久久久成人AV| 精品久久久久久久久午夜福利| 久久久国产99久久国产一| 免费久久人人爽人人爽av| 亚洲第一永久AV网站久久精品男人的天堂AV| 久久精品国产只有精品2020| 青青青国产成人久久111网站|