背景:在c風格語言中(比如c,c++,c# (注:排名按出生日期 ^_^)),取餘運算子定義為「%」。但在很久很久以前,cpu採用如下方法計算餘數(注意,該方法只對2的n次方數系有效):
x & (2^n - 1)
看到這個演算法,我感到比較好奇,為什麼這麼做呢?
首先從求餘數談起,我們知道,計算機中儲存的方式是0和1序列:
1 0001 2^0
2 0010 2^1
3 0011 2^1 + 1
4 0100 2^2
當我們把這些數字序列左移一位的時候... 是的,你答對了,相當與數字擴大為原來的2倍,同理可知右移一位就是縮小為原來的1/2。
那麼餘數在**?被移掉的哪些位便是餘數,這是無數前人的證明結果~可以自己拿筆和紙畫一畫就明白了。
那麼,說了這麼多,為什麼乙個求餘的%被替換為:x & (2^n - 1)?
舉例:9 的二進位制就是1001
8 的二進位制就是1000
顯然餘數是1,按照公式這麼做:
把8的二進位制1000換為8-1也就是7,7的二進位制表示為:0111
1001&0111 = 0001
答案為1與我們想的結果一致。
是巧合麼?我們把9換成13,過程你親自來一遍,結果在你沒算錯的情況下必然和我的一樣:0101
但是注意,這種方法只是適合與求乙個數除以二的n次冥才正確。
先說原理再說疑問:
原理:由於除數是2^n (n為0起始遞增的整數),所以隱藏條件就是,除數的2進製真身只能是如下的方式展現:
0001,0010,0100,1000.。。。。。
沒看出來?那我們繼續:
10000,100000,1000000.。。。。
明白了吧,當我們求餘的時候,相當於除以2的n次冥,也就是相當於把數本身右移n位,但是移走的那些餘數可一去不復返了。(什麼?你說在cf暫存器中?嗯,這是個好注意,也許有另種一實現?!)。有什麼辦法保護這些即將失去的資料呢?
我們把上面的數字減一就發現:
0000,0001,0011,0111,01111,011111,0111111.。。。。。。
如您所見,當和1做位運算時候,得到的結果是那個數的本身。前面例子1001&0111= 0001相當於1001右移三位得到0001,那麼(13)1101右移三位還是0001,這2個0001表示的是商,兩個移走的餘數分別是「001」和「101」,發現特徵了嚒?除以2的n次方就是要儲存被除數即將被移走的低n位。
好了,我知道上面我講的可能不清楚,但是下面才是真正驚險的地方:
2^3=8 然後8表示為1000 有3個零,預示著右移位數,右移位數告訴我們被除數即將被移走幾位,我們如果將這幾位與1按位運算不就能知道這幾位到底是0還是1,也就是能完全儲存到移走的資料,它就是我們期望的餘數。
嗯,我確信看完我的解釋你還在暈眩之中,別管他們,該幹嘛幹嘛,睡完一覺之後我想我的這些話也許會被你的大腦自動解析,你最終也會明白這是怎麼一回事。
也許還有極個別頭腦超好的人並切超有毅力的看到這裡,他們會問,你說的疑問是什麼?
嗯,我想說,如果早起電腦都這麼做的話,那麼求乙個非2的n次冥的取餘怎麼來實現呢?
如果你知道,請告訴我^_^
位運算子與位運算
位運算是對二進位制位的操作,它應用於整形資料,把整形資料看成固定的二進位制序列,然後對二進位制序列進行位運算 按位與運算 按位與運算子 是雙目運算子。其功能是參與運算的兩數各對應的二進位相與。只有對應的兩個二進位均為1時,結果位才為1 否則為0。參與運算的數以補碼方式出現。例c a b a 1010...
進製運算與位運算
不可以在二進位制表示形式中使用負號 5 0000 0101 5 1111 1011 取反加一 既是補碼 負數二進位制的計算方法是把整數二進位制按位求反 再加一 就是補碼 每個有符號資料型別中首位為 1 其它位都是 0 的二進位制表示這個資料型別的最小數 採用剛才的計算方法同樣可以根據負數的二進位制補...
位運算與移位運算
位運算指的是進行二進位制位的運算 位運算子說明 取反 按位與 按位或 按位異或 左移運算子,左移一位等於乘二 右移運算子,右移一位相當於除2取商 按位取反 按位與 按位或 按位異或的 執行操作 int a 7 0111 int b 8 1000 system.out.println a b 按位與結...