網(wǎng)站備案 godaddyseo公司上海牛巨微
問題:
- 我們每天用的鐘表,其實只有1~12這12個數(shù)字,但我們?nèi)粘f13點、17點之類的。
問:13點在鐘表上哪個位置?
答:很簡單嘛,1點的位置。
你不覺得奇怪嗎,為啥13點會和1點在同一個位置?換言之,13和1有啥關(guān)系,17和5有啥關(guān)系? - 計算機里為啥要用加法替代減法?
- 當然是加法電路設(shè)計比減法電路設(shè)計簡單了,加法電路詳見CPU之圖解算數(shù)邏輯單元ALU
- 怎么用加法替代減法?
一、基本概念
1.0、余數(shù)(remainder)
在算術(shù)中,當兩個整數(shù)相除的結(jié)果不能以整數(shù)商表示時,余數(shù)便是其“余留下的量”。當余數(shù)為零時,被稱為整除。
即:被除數(shù) / 除數(shù) = 商……余數(shù),用數(shù)學表示:
a = qd + r, 0 <= r < d
,其中a為被除數(shù),q為商(quotient),d為除數(shù)(divisor),r為余數(shù)(remainder)。
舉例:當除數(shù)為4
時,任何正數(shù)除以4
時,余數(shù)總在0, 1, 2, 3
中;
0 / 4 = 0 …… 0
1 / 4 = 0 …… 1
2 / 4 = 0 …… 2
3 / 4 = 1 …… 3
4 / 4 = 1 …… 0
5 / 4 = 1 …… 1
6 / 4 = 1 …… 2
7 / 4 = 1 …… 3
8 / 4 = 2 …… 0
……
如上,被除數(shù)從0
開始遞增時,余數(shù)會在0, 1, 2, 3
中一直循環(huán)下去,這就是同余
現(xiàn)在我們考慮除數(shù)為10
時,則余數(shù)在0~9
這10個數(shù)字中出現(xiàn);把這10個數(shù)字同樣置于圓盤中,如上圖所示。
在0-9
這10個數(shù)字的圓盤中,再也不會有其它數(shù)字了,也就是我們限定了我們的數(shù)字范圍為0~9
,數(shù)字個數(shù)(記為模)為10
。
給定圓盤上任一個數(shù)字作為起始點(記為 src),任一個數(shù)字為 終止點(記為dest),我們考慮從src出發(fā),如何到達dest,可以 前進(記為 + ),可以后退(記為 - )。
舉例:
src = 3,dest = 4;
- 前進1步(+1),即 3 + 1 = 4;
- 后退9步(-9), 即 3 - 9 = 4;
src = 3,dest = 3;
- 前進0步(+0),即 3 + 0 = 3;
- 后退(9+1)步(-(9+1)), 即 3 - ( 9 + 1) = 4;// 因為我們限定了數(shù)字范圍為0~9,所以寫成(9 + 1)
src = 5,dest = 9;
- 前進4步(+4),即 5 + 4 = 9;
- 后退6步(-6), 即 5 - 6 = 9;
通過上面的例子,發(fā)現(xiàn)了嗎?
- 對于確定的起止點(如src = 3,dest = 4),不管前進或者后退,我們都可以到達,也就是說前進(1步)等價于后退(9步),
- 前進步數(shù)和后退步數(shù)之和為模(10),模意味著啥?
- 模意味著走了一圈,一個輪回,起點走一圈,又回到了起點
1.1、補數(shù)(complement)
定義:對于給定的進位制,相加后能使自然數(shù) a 的位數(shù)增加 1 的最小的數(shù)。
說人話:就是前面前進和后退的步數(shù)等價的組合(如1和9、2和8、3和7、4和6),也即可以組成模(如10)的組合。
即:
當 a = 1時,a只有1位數(shù)字(即個位)要想將a變成2(1+1)位數(shù)字10(十位、個位),需要增加9。
所以 9是1的補數(shù);當然1也是9的補數(shù),即1、9互為補數(shù);
同理:2、8互為補數(shù);
3、7互為補數(shù);
4、6互為補數(shù);
問: 36的補數(shù)是多少?
答:64
根據(jù)上面定義,我們知道對于整數(shù)a,a的補數(shù) = 模 - a(a>=0);
補數(shù)有啥用?
還是回到之前我們0~9
這10
個數(shù)字的圓盤上來,我們來算小學生都會的10以內(nèi)的加減法:
一頓操作猛如虎,一看結(jié)果:直呼OMG,
正如上圖所示:減掉一個數(shù),等價于加上這個數(shù)的補數(shù)。
這里有個重要的前提:數(shù)字是有范圍的(上面我們限定了0~9這10個數(shù)字)
1.2、減法
根據(jù)我們上面的結(jié)論:當數(shù)字是有范圍的,則對于這個范圍內(nèi)的數(shù)字,減掉一個數(shù),等價于加上這個數(shù)的補數(shù),我們可以用加法替代減法,進一步:減去一個正數(shù)相當于加上這個正數(shù)對應(yīng)的負數(shù)。
還是回到我們前面提到的圓盤上來,我們把這條結(jié)論實踐下:
- 我們先在坐標軸上標出0~9這10個數(shù)字范圍,然后移動這個范圍框,使框內(nèi)正負數(shù)個數(shù)大致相等,如下圖所示,數(shù)字范圍變成了
-5 ~ 4
這10個數(shù)字。
- 將坐標軸上的數(shù)字范圍同步映射到圓盤上。
- 此時,圓盤上數(shù)字的運算可以用圓盤外相對應(yīng)的數(shù)字進行替代,即圓盤上的數(shù)字范圍已經(jīng)改變了,由之前的0 ~ 9 變成了現(xiàn)在的 -5 ~ 4 。
1.3、補碼
看到這里,不知道你是否認可前面的推導結(jié)論?
不認可?不認可就對了。聰明的你一定發(fā)現(xiàn)了上面的計算的問題:
1 - 5 = -4,在我們映射后變成了 1 + 5 = 6 但 -4 不等于 6?雖然圓盤上6確實對應(yīng)**-4**,但我們需要的是正確的結(jié)果-4,怎么將6轉(zhuǎn)換成-4?
- 好了,為了后面好敘述,我們先給實際求值結(jié)果記為原碼,映射后的結(jié)果記為補碼。我們先分析下實際的求值結(jié)果和映射后的結(jié)果:
- 發(fā)現(xiàn)原碼為0和正數(shù)時,補碼正確,即原碼為0和正數(shù)時,原碼等于補碼;
- 原碼為負數(shù)時,補碼錯誤,即原碼為負數(shù)時,原碼不等于補碼。
問題來了?原碼為負數(shù)時,補碼(如6)怎么轉(zhuǎn)換成原碼(如-4)?
答: 謎底就在謎面上,你直接看圓盤不就好了,圓盤上都標注了,圓盤內(nèi)外側(cè)的數(shù)字就是我們原碼和補碼的映射表。
問題又來了?圓盤上的映射怎么來的?
答:通過補數(shù)變換得到的,即原碼為負數(shù)時,補碼 + |原碼| = 模,也即原碼為負數(shù)時,補數(shù) + |原碼| = 模,在1.1小節(jié)中,我們提到了 a的補數(shù) = 模 - a(a>=0);
,
綜上:原碼為負數(shù)時,補碼 + |原碼| = 模
二、 二進制里的花花世界
宏觀世界搞定了,看看微觀世界,十進制搞完了,看看二進制。
二進制我們以1Byte(8bit)研究,1 Byte 可以表示256個數(shù)(28),即模為256
2.1 原碼
1 Byte 范圍的內(nèi)數(shù)字為0~255
這256個數(shù)字。將其置于圓盤上如下:
2.2 補碼
- 調(diào)整數(shù)字范圍使其映射到 -128~127 這256個數(shù)字:
- 調(diào)整圓盤范圍,即原碼寫到圓盤上,補碼寫到圓盤外,如下:
2.3 補碼和原碼轉(zhuǎn)換
原碼和補碼怎么轉(zhuǎn)換呢?
通過1.3節(jié),我們知道如下結(jié)論:
- 原碼為0和正數(shù)時,原碼等于補碼
- 原碼為負數(shù)時,補碼 + |原碼| = 模
- 還記得我們在1.3 小節(jié) 文末得到的結(jié)論嗎?原碼為負數(shù)時,補碼 + |原碼| = 模,我們變換下這個等式,即原碼為負數(shù)時,補碼 = 模 - |原碼|,按照公式我們計算下:
至此,二進制的原碼和補碼轉(zhuǎn)換我們也已搞定,即: - 原碼為0和正數(shù)時,原碼等于補碼
- 原碼為負數(shù)時,補碼 = 模 - |原碼|
2.4 反碼
不知道你繞暈了嗎?還記得我們的初心嗎?怎么用加法替代減法?
2.3 節(jié)說原碼為負數(shù)時,補碼 = 模 - |原碼|,這玩呢?前面這么多推理就是想用加法替代減法,結(jié)果搞了半天回到了原點,還使得計算減法,自己替代自己嗎?替代了個寂寞……
不急,稍安勿躁,且聽下面道來。
2.4.1 模
細想下 :補數(shù)的定義,使得當前數(shù)的位數(shù)增加1位:
- 假設(shè)二進制下,當前為8位,問要使得增加的數(shù)最小時,怎么才能使得位數(shù)增加1位呢,即由原來的8位變成9位?
答:增加的數(shù)為1時最小,此時當前8位數(shù)達到了最大,那就是
0b 1111 1111
,即0b 1111 1111
+0b 0000 0001
=0b 1 0000 0000
- 所以我們找到了二進制8位數(shù)即將跳變成9位的臨界值,即當前8位數(shù)的最大值
0b 1111 1111
; - 二進制時,一個數(shù)變成最大的最快方式,當然是加上該數(shù)按位取反后的值。即
0b 1111 1111
-0b 0000 0001
=0b 1111 1110
,看到了嗎,0b 1111 1110
正好等于0b 0000 0001
按位取反的值,那就叫它反碼吧,即原碼為負數(shù)時,反碼為原碼數(shù)據(jù)位按位取反,注意這里是數(shù)據(jù)位,最高位為符號位“為使得定義一致和完整,我們補充:原碼為0和正數(shù)時,原碼等于反碼。
- 我們來求下 模
- 如上圖所示,原碼為負數(shù)時,模 (256) = 8bit最大值 + 1 , 8bit最大值 = 原碼的絕對值 + 反碼,結(jié)合之前的 原碼為負數(shù)時,補碼 = 模 - |原碼| 得到:
至此,1 Byte 的數(shù)據(jù)我們通過補碼的形式用加法替代了減法。計算機語言內(nèi)的整數(shù)類型比如java
的int (4Byte)、long(8Byte)
都是如此。
三、總結(jié)
為了使用加法替代加法,我們先后引入了補數(shù)、模、原碼、補碼、反碼等概念:
- 圓盤代表數(shù)字是有范圍的,該范圍的大小即為模,并且會首尾相連。計算機1 Byte、4Byte、8Byte天然會限定數(shù)據(jù)的范圍。數(shù)據(jù)范圍限定后,數(shù)據(jù)一直累計下去一定會產(chǎn)生數(shù)據(jù)溢出(即進位丟失),從而結(jié)束當前輪回,開始下一輪回;
- 圓盤內(nèi)的數(shù)字是原碼,圓盤外的數(shù)字是補碼,補碼也是模內(nèi)的無符號整數(shù)。注意,反碼 + 1只是求得補碼的一種方式,并非補碼的定義;
- 原碼為0和正數(shù)時,原碼等于反碼等于補碼;
- 原碼為負數(shù)時,反碼為原碼數(shù)據(jù)位按位取反。