• <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++之竹

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

            常用鏈接

            統(tǒng)計

            最新評論

            不使用 +-×÷ 運算符來實現(xiàn) 加減乘除 四項運算

            最近,在看《劍指Offer——名企面試官精講典型編程題》一書,當看到“面試題47:不用加減乘除做加法”時,經(jīng)過一分鐘左右思考后,得出了思路,跟書上一對照,基本一致,呵呵O(∩_∩)O~。于是,隨即又開始思考:加法是實現(xiàn)了,那么減法、乘法還有除法又該怎么實現(xiàn)呢?一番思考與分析后,得出算法,寫出代碼,測試通過,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函數(shù)的實現(xiàn)

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

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

            代碼2.1 取整數(shù)n的相反數(shù)

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

            然后就可以通過Add函數(shù)來實現(xiàn)減法了。

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

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

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

            方案二:直接通過位操作實現(xiàn)減法

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

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

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

            代碼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;
            }

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

             【3. 乘法】

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

            代碼3.1 輔助宏與輔助函數(shù)

            #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. 整數(shù)n乘以2k == n << k
            2. C++中的任何一個非負int型數(shù)據(jù)都可以表示為如下的形式:
              k0×20+k1×21+...+km×2m
              的形式。(其中:ki∈{0, 1}, i∈{0, 1, ... , m}, 32位int型m = 30, 64位int型m = 62)

            于是,就可以利用乘法分配率,通過循環(huán)累加,進行乘法的運算了。參考代碼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. 除法】

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

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

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

            對應的代碼參加代碼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);
            }

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

             

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

            評論

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

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

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

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

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

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

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


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

              回復  更多評論   

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

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

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

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

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

            @SwordLance
            是的。  回復  更多評論   

            久久婷婷五月综合国产尤物app | 精品无码久久久久久尤物| 亚洲午夜精品久久久久久app| 久久青青国产| 国产成人无码久久久精品一| 久久国产综合精品五月天| 日韩久久无码免费毛片软件| 久久中文骚妇内射| 亚洲午夜久久久| 国内精品久久久久久久涩爱| 久久99国产精品久久99小说| 久久香蕉国产线看观看乱码| 久久人人添人人爽添人人片牛牛 | 久久这里有精品| 国产精品伦理久久久久久| 亚洲综合日韩久久成人AV| 久久人人爽人爽人人爽av| 欧美伊香蕉久久综合类网站| 久久久噜噜噜久久中文字幕色伊伊 | 久久久久久精品成人免费图片| 色综合久久综精品| 久久婷婷五月综合色奶水99啪| 中文成人无码精品久久久不卡| 激情综合色综合久久综合| 91精品国产91久久久久福利| 一个色综合久久| 久久天天躁狠狠躁夜夜躁2014| 久久久网中文字幕| 久久夜色撩人精品国产| 久久av免费天堂小草播放| 久久99精品久久久久久水蜜桃 | 色综合久久久久综合体桃花网| 亚洲七七久久精品中文国产| 色诱久久av| 99久久精品免费看国产一区二区三区 | 香蕉99久久国产综合精品宅男自 | 久久精品国产亚洲AV电影 | 欧美综合天天夜夜久久| 精品亚洲综合久久中文字幕| 久久电影网2021| 伊人久久大香线焦综合四虎|