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