學習編程的過程中,計算兩個數(shù)的和 int a=b + c 這樣的代碼肯定經(jīng)常寫到,但是計算機到底是怎么計算出來的呢?
說到加法,首先提到的一個概念就是全加器,下圖是一個全加器的數(shù)字邏輯電路。
其中,異或門的輸出 Y=A ^ B ,與非門的輸出就是先與再非,即 Y=!(A & B) 。這里我就不再列真值表了,我們再來看一下上面全加器的邏輯,通過推導可以得到下面公式:
Sum=A ^ B ^ Cin
Cout=(A ^ B & Cin) | (A & B)
這是一位(1 bit)的加法運算,當我們把 8 個全加器級聯(lián)起來的話(低位的 Cout 是高位的 Cin )就實現(xiàn)了一個 8 位的加法器。我們可以用代碼模擬一下(LeetCode 371. 兩整數(shù)之和):
說到這里,大家應(yīng)該大體的了解計算機是怎么計算加法的了,那減法呢?
加法是進位,減法需要考慮的則是借位,是這樣嗎?按我們小時候?qū)訙p法學習的經(jīng)驗是這樣的,但是計算機不是這么處理的。計算機“只有加法”,“沒有減法”。肯定有同學說,不可能,那我們算 int a=b - c 是怎么得出來結(jié)果的呢?說到這個首先要了解另外一個概念——補碼。
補碼
我們知道,計算機中對于有符號數(shù),用最高位作為符號位,“0” 代表 “+” ,“1” 代表 “-” ;其余數(shù)位用作數(shù)值位,代表數(shù)值。比如 Byte 類型的取值范圍為 -128 ~ 127。其中,表示數(shù)值的只有 7 位,首位表示正負。
怎么推導一個數(shù)字的補碼呢?補碼規(guī)定,正數(shù)和 0 的補碼就是其原碼(原碼、反碼的定義這里就不多贅述),負數(shù)的補碼是其正數(shù)的原碼取反再加 1 。
聽起來有點繞,舉個例子,求 -10 的補碼:
十進制 10 的原碼(按 8 位舉例)為 0000 1010,其反碼為 1111 0101,取反后再加 1 即為其補碼: 1111 0110。因此,-10 的補碼為 1111 0110。
不知道寫到這里,大家有沒有發(fā)現(xiàn)什么端倪?我們再回到減法計算來。
a=b - c
實際上等同于
a=b + ( -c )
我們算一下下面的式子:
通過這兩個例子是不是就清楚了計算機是如何計算減法的了?
通過說減法,我們是不是對乘法也有一定的啟發(fā)了呢?乘法其實就是循環(huán)的加法。比如 5 * 3 實際上就是 5 + 5 + 5。貌似就說完了。實際上不僅僅如此的。現(xiàn)在有一個電子器件叫做乘法器,其可以實現(xiàn)二進制的乘法、除法等運算。我們同樣以 5 * 3 做為例子,講解一下乘法器計算乘法的流程。
3 的第 0 位為 1 ,那么 5 左移 0 位,結(jié)果為 0000 0101 ;
3 的第 1 位為 1 ,那么 5 左移 1 位,結(jié)果為 0000 1010 ;
3 的其他高位都為 0 ,因此不再左移;
兩次左移的結(jié)果累加,即 0000 0101 + 0000 1010=0000 1111 ,即十進制的 15 。
雖然有乘法器,但是我們發(fā)現(xiàn)實際的最終操作流程還是加法和位移操作計算的乘法運算。我們寫的代碼中的乘法到底是用乘法器算的還是轉(zhuǎn)化成加法運算,我們也并不太確定,有些編譯器編譯的時候會對代碼進行優(yōu)化,會選取最優(yōu)的一種算法來計算結(jié)果。
除法可以通過減法來實現(xiàn),比如 10 / 3 等價于 10 一直減 3 直到被減數(shù)小于 3 ,減了 3 次,那么 10 / 3 的結(jié)果就為 3 了,余數(shù)為減完剩下的值 1 。
其實上面已經(jīng)提到了乘法器,除法的原理同樣也類似(這里不說浮點數(shù)的除法,只說整數(shù)的除法),但是稍微復(fù)雜一點。( LeetCode 29. 兩數(shù)相除)
同樣我們舉個例子來說明一下。
首先取 209 的最高位 1 (101 0001) 小于 101
左移一位 11 (01 0001) 小于 101
左移一位 110 (1 0001) 大于 101 ,商 1 ,余 1 (1 0001)
左移一位 11 (0001) 小于 101
左移一位 110 (001) 大于 101 ,商 1 ,余 1 (001)
左移一位 10 (01) 小于 101
左移一位 100 (1) 小于 101
左移一位 1001 大于 101 ,商 1 ,余 100
于是結(jié)果為 00101001 (41),余數(shù)為 100 (4)。
通過上面我們發(fā)現(xiàn),計算機計算加減乘除,都是轉(zhuǎn)換為加法和位移運算完成的,而事實上也是如此。可能我們平時編程過程中可能用不到這些內(nèi)容,直接寫 + - * / 就可以了,但是作為一名程序員,還是有必要了解一下計算機基礎(chǔ)的編碼以及基礎(chǔ)的運算的。祝大家學習愉快!
由計算機的硬件決定,任何存儲于計算機中的數(shù)據(jù),其本質(zhì)都是以二進制碼存儲。
根據(jù)馮~諾依曼提出的經(jīng)典計算機體系結(jié)構(gòu)框架。一臺計算機由運算器,控制器,存儲器,輸入和輸出設(shè)備組成。其中運算器,只有加法運算器,沒有減法運算器(據(jù)說一開始是有的,后來由于減法器硬件開銷太大,被廢了 )
所以,計算機中的沒法直接做減法的,它的減法是通過加法來實現(xiàn)的。你也許會說,現(xiàn)實世界中所有的減法也可以當成加法的,減去一個數(shù),可以看作加上這個數(shù)的相反數(shù)。當然沒錯,但是前提是要先有負數(shù)的概念。這就為什么不得不引入一個該死的符號位。
而且從硬件的角度上看,只有正數(shù)加負數(shù)才算減法。 正數(shù)與正數(shù)相加,負數(shù)與負數(shù)相加,其實都可以通過加法器直接相加。
原碼、反碼、補碼的產(chǎn)生過程就是為了解決,計算機做減法和引入符號位(正號和負號的問題)
在了解什么是原碼、反碼、補碼前我們先了解什么無符號數(shù)和有符號數(shù)
沒有正負之分
例C語言中 unsigned int 類型
寄存器的位數(shù)反映無符號數(shù)的表示范圍
有符號數(shù)包含符號部分和數(shù)值部分
一般我們規(guī)定有符號數(shù)機器數(shù)的第1位為符號位,1代表這個數(shù)是負數(shù),0代表這個數(shù)是整數(shù)
假設(shè)已C語言中 char類型舉例(默認為有符號數(shù))
char類型為1Byte,8bit
那么char類型能表示的范圍是多少?
應(yīng)該是-128~127
要知道為什么是-128~127我們就需要知道數(shù)據(jù)在計算機中是怎么存儲的,下面引入原碼、反碼、補碼以及移碼的概念
原碼:是最簡單的機器數(shù)表示方法。用最高位表示符號位,‘1’表示為負號,‘0’表示為正號,其他位存放該數(shù)的二進制的絕對值
1010:最高位為‘1’,表示這是一個負數(shù)
其他三位為‘010’,轉(zhuǎn)換為十進制為:2
所以1010表示十進制為:-1
1010:最高位是‘1’還是‘0’無所謂了,因為它表示為無符號數(shù)
所以直接轉(zhuǎn)換為十進制為:10
下圖給出部份正負數(shù)數(shù)的二進制原碼表示法
OK,原碼表示法很簡單有沒有,雖然出現(xiàn)了+0和-0,但是直觀易懂。 于是,我們高興的開始運算。
0001+0010=0011 (1+2=3)OK
0000+1000=1000 (+0+(-0)=-0) 額,問題不大
0001+1001=1010 (1+(-1)=-2) ???
噢,1+(-1)=-2,我趣里的
于是我們可以看到其實正數(shù)之間的加法通常是不會出錯的,因為它就是一個很簡單的二進制加法。
而正數(shù)與負數(shù)相加,或負數(shù)與負數(shù)相加,就要引起莫名其妙的結(jié)果,這都是該死的符號位引起的。0分為+0和-0也是因他而起。
所以原碼,雖然直觀易懂,易于正值轉(zhuǎn)換。但用來實現(xiàn)加減法的話,運算規(guī)則總歸是太復(fù)雜。于是反碼來了。
我們知道,原碼最大的問題就在于一個數(shù)加上他的相反數(shù)不等于零。
例如:
于是反碼的設(shè)計思想就是沖著解決這一點,既然一個負數(shù)是一個正數(shù)的相反數(shù),那我們干脆用一個正數(shù)按位取反來表示負數(shù)試試。
反碼:
正數(shù)的反碼還是等于原碼
負數(shù)的反碼就是他的原碼除符號位外,按位取反。
若以帶符號位的四位二進制數(shù)為例:
下圖給出部分正負數(shù)的二進制數(shù)反碼表示法
對著上圖,我們再試著用反碼的方式解決一下原碼的問題
好,我們再試著做一下兩個負數(shù)相加(運算中均為反碼)
噢,好像又出現(xiàn)了新問題
不過好像問題不大,因為1011(是-4的反碼,但是從原碼來看,它其實是-3。巧合嗎?)
我們再看個例子吧
確實是巧合,看來相反數(shù)問題是解決了,但是卻讓兩個負數(shù)相加的出錯了。
但是實際上,兩個負數(shù)相加出錯其實問題不大。我們回頭想想我們的目的是什么?是解決做減法的問題,把減法當成加法來算。
兩個正數(shù)相加和兩個負數(shù)相加,其實都是一個加法問題,只是有無符號位罷了,而正數(shù)+負數(shù)才是真正的減法問題。
也就是說只要正數(shù)+負數(shù)不會出錯,那么就沒問題了。負數(shù)加負數(shù)出錯沒關(guān)系的,負數(shù)的本質(zhì)就是正數(shù)加上一個符號位而已。
但是我們還是不滿足為什么 0001+1110=1111 (1+(-1)=-0) 為什么是-0呢?
下面有請補碼!
補碼:
正數(shù)的補碼等于他的原碼
負數(shù)的補碼等于反碼+1。 (這只是一種算補碼的方式,多數(shù)書對于補碼就是這句話)
OK,補碼就講完了。再見!!!
莫名其妙有沒有,為什么補碼等于反碼加1,為什么……………….?
其實上面那段話,只是補碼的求法,而不是補碼的定義。很多人以為求補碼就要先求反碼,其實并不是。
那些雞賊的計算機學家,并不會心血來潮的把反碼+1就定義為補碼。只不過是補碼正好就等于反碼加1罷了。
下面詳細介紹補碼的思想,可以跳過,你只需要記住補碼怎么計算就行。但是如果你肯停下來仔細想想,絕對會覺得非常美妙,極其優(yōu)雅!
補碼的思想就類似于生活中的時鐘
如果現(xiàn)在時針停在10點鐘,那么怎樣讓時鐘調(diào)整到八點鐘的位置呢?
有兩種方法:
也就是說時間正撥10小時,或是倒撥2小時都是八點鐘。
也就是10-2=8,而且 MOD[(10+10),12]=8
既然是等效的,那在時鐘運算中,減去一個數(shù),其實就相當于加上另外一個數(shù)(這個數(shù)與減數(shù)相加正好等于12,也稱為同余數(shù))
這就是補碼所謂模運算思想的生活例子
在這里,我們再次強調(diào)原碼,反碼,補碼的引入是為了解決做減法的問題。在原碼,反碼表示法中,我們把減法化為加法的思維是減去一個數(shù),等于加上一個數(shù)的相反數(shù),結(jié)果發(fā)現(xiàn)引入了符號位,卻因為符號位造成了各種意向不到的問題。
但是在補碼表示法中,我們可以看到其實減去一個數(shù),對于數(shù)值有限制,有溢出的運算(模運算)來說,其實也相當于加上這個數(shù)的同余數(shù)。
也就是說,我們不引入負數(shù)的概念,就可以把減法當成加法來算(模運算)。
補碼的思想介紹完畢
下圖給出帶符號位四位二進制的補碼表示法
到這里,我們發(fā)現(xiàn)原碼,反碼的問題,補碼基本解決了。
在補碼中也不存在負零了,因為1000表示-8
這是因為根據(jù)上面的補碼圖,做減法時,0001(1)+1111(-1)=0000 ,我們再也不需要一個1000來表示負0了,就把它規(guī)定為-8
然后我們再來看看為什么負數(shù)的補碼的求法為什么是反碼+1
假設(shè)一個四位負數(shù)為1001,即(-1)
1110+0001+1
=1111+1
=10000,即四位數(shù)的模(指四位二進制數(shù)所能表示的最大數(shù) 16)
立即推:負數(shù)的反碼 + 負數(shù)的絕對值 + 1=模
而:負數(shù)的補碼是它絕對值的同余數(shù),即補碼 + 負數(shù)的絕對值=模
立即推:補碼 + 負數(shù)的絕對值=負數(shù)的反碼 + 負數(shù)的絕對值 + 1
得:補碼=負數(shù)的反碼 + 1
妙不妙(這玩意是怎么研究出來的)
為什么這里一直在說負數(shù)呢?
因為正數(shù)的原碼、反碼、補碼都一樣,不用計算
到這里補碼的概念也介紹完了,我們回到剛才的問題:為什么C語言中char類型的范圍為:-128~127
直接上圖解:
如果是unsigned char 那能表示范圍為:0~255
如果我們把-8當成負數(shù)的原點。那么-5的補碼是多少呢?
所以完全可以口算出-5的補碼是1011
對于八位加法器的話,可以把-128當補碼原點。十六位可以把-32768當補碼原點。
是的,128是256(八位二進制數(shù)的模)的一半,32768是65536(十六位二進數(shù)的模)的一半
也很方便有沒有,而且簡單的是
補碼原點總是最高位是‘1’,其他位是‘0’
所以看上圖的-128(10000000) 就可以當作補碼的原點,-127 就是-128+1,即(10000001)
數(shù)學之美
移碼(又叫增碼或偏置碼)通常用于表示浮點數(shù)的階碼,其表示形式與補碼相似,只是其符號位用“1”表示正數(shù),用“0”表示負數(shù),數(shù)值部分與補碼相同。
移碼與補碼的關(guān)系: [X]移與[X]補的關(guān)系是符號位互為相反數(shù)(僅符號位不同),
所以,移碼很簡單,不管正負數(shù),只要將其補碼的符號位取反即可。
即知道補碼,將其符號位取反就是移碼
知道移碼將其符號位取反就是補碼
補碼表示很難直接判斷其真值大小,比如:
我們加一個偏移量:2^5,得到正確的大小比較結(jié)果
如果是有符號數(shù),去除符號位
從這也可以看出上文中C語言中char類型能表示的最小值為啥是:-128
char類型默認是有符號數(shù),1Byte 8bit,第1位是符號位,所以真值的位數(shù)n=7
最小值為:-27
上面叭叭說了一大段,總結(jié)下來就幾條。但是如果你肯停下來琢磨是什么原理,你會發(fā)現(xiàn)數(shù)學的美妙
小數(shù)點固定在某一位置的數(shù)稱為定點數(shù),有以下兩種格式
采用定點數(shù)表示方式的機器稱為定點機
在這種表達方式中,小數(shù)點的位置固定不變,一般用來表示一個純小數(shù)或者整數(shù)。
數(shù)的定點表示熟知的取值范圍有限,表示一個純小數(shù)時,小數(shù)點固定在符號位之后;表示一個整數(shù)時,小數(shù)點固定在數(shù)據(jù)最后一位之后。
純小數(shù)即(0,1)之間的小數(shù)
小數(shù)定點機中數(shù)的表示范圍為:-(1-2^-n)~(1-2^-n)
整數(shù)定點機中數(shù)的表示范圍為:-(2^n - 1)~(2^n - 1)
例:假設(shè)機器數(shù)字長為8為,符號位為1位,用定點法表示10和-10
10用二進制表示為:1010
因為字長為8為所以高位補0
則10用定點法表示為:01010000
-10用定點法表示為: 11010000
例:假設(shè)機器數(shù)字長為8為,符號位為1位,用定點法表示 0.5 和 -0.5
0.5=1/2^1 即小數(shù)點向左移1位
所以二進制形式為:0.1
又因為字長為8
所以二進制形式為:0.1000000
對于 -0.5只需將符號位取反 即 1.1000000
定點數(shù)表達法的缺點在于其形式過于僵硬,固定的小數(shù)點位置決定了固定位數(shù)的整數(shù)部分和小數(shù)部分,不利于同時表達特別大的數(shù)或者特別小的數(shù)。
所以使用浮點數(shù)表示的原因:
定點數(shù)的表示范圍小,為了能表示兩個大小相差很大的數(shù)據(jù),需要很長的機器字長,導致數(shù)據(jù)存儲單元的利用率往往很低
最終,絕大多數(shù)現(xiàn)代的計算機系統(tǒng)采納了所謂的浮點數(shù)表達方式。
這種表達方式利用科學計數(shù)法來表達實數(shù),即用一個 尾數(shù),一個 基數(shù),一個 階數(shù)以及一個 符號位來表達實數(shù)。
比如 123.45 用十進制科學計數(shù)法可以表達為 (-1)^0 x 1.2345 × 10^2 ,其中 (-1)^0的0表示符號位(備注:如果表示負數(shù),符號位為1),1.2345 為尾數(shù),10 為基數(shù),2 為階數(shù)。浮點數(shù)利用指數(shù)達到了浮動小數(shù)點的效果,從而可以靈活地表達更大范圍的實數(shù)。
這個計算公式看起來比較復(fù)雜,其實就是計算特定數(shù)取得最大時的數(shù)據(jù)范圍,仔細分析就可以得出來了,沒啥好糾結(jié)的。
例題:
問題:設(shè)機器數(shù)字長為 24 位,欲表示± 3 萬的十進制數(shù), 試問在保證數(shù)的最大精度的前提下,除階符、數(shù)符各 取 1 位外,階碼、尾數(shù)各取幾位?
分析:
為什么要進行浮點數(shù)的規(guī)格化?
對浮點數(shù)進行規(guī)格化的主要目的是盡可能的保證數(shù)據(jù)的精度。
如果不進行規(guī)格化,尾數(shù)的小數(shù)點后邊可能會有若干個0,0占據(jù)了尾數(shù)表示有效數(shù)據(jù)(不為0的數(shù)據(jù))的字長,導致可以表示有效數(shù)據(jù)的字長變短,降低數(shù)據(jù)的精度。
因此,我們對浮點數(shù)進行規(guī)格化,就要使尾數(shù)中表示有效數(shù)據(jù)的位置盡可能的多。
浮點數(shù)的規(guī)格化形式
比如
因此,基數(shù)不同,浮點數(shù)的規(guī)格化形式不同,規(guī)格化形式不同,基數(shù) r 越大,浮點數(shù)的精度降低
如何進行規(guī)格化?
對浮點數(shù)進行規(guī)格化實際上就是把尾數(shù)部分多余的0移除掉,通過操作階數(shù)來保證原來的數(shù)值(真值)不變,這種方式稱為做規(guī)。
浮點數(shù)表示例題:
將
寫成二進制定點數(shù)、浮點數(shù)及在定點機和浮點機中的機器數(shù)形式。其中數(shù)值部分均取 10 位,數(shù)符取 1 位,浮點數(shù)階碼取 5 位(含1位階符),尾數(shù)規(guī)格化
解:設(shè)x=
二進制形式: x=0.0010011 ——> 10011(19) / 2^7(128) ——>小數(shù)點向左移7位
定點表示: x=0.0010011 000 ——> 數(shù)值部分均取 10 位,所以擴展3位0
浮點規(guī)格化形式 :
——> -10(2)為二進制碼,表示向右移動兩位小數(shù)點
將 –58 表示成二進制定點數(shù)和浮點數(shù), 并寫出它在定點機和浮點機中的三種機器數(shù)及階碼為移碼、尾數(shù)為補碼的形式(其他要求同問題一)。
機器零
移位運算在日常生活中常見。例如:
計算機中小數(shù)點的位置是事先約定的,因此,二進制表示的機器數(shù)在相對于小數(shù)點作n位左移或右移時,其實質(zhì)就便該數(shù)乘以或除以2^n(n=1,2...n)。
移位運算又叫移位操作,對計算機來說,有很大的實用價值,例如,當計算機沒有乘(除)運算線路時,可以采用移位和加法相結(jié)合,實現(xiàn)乘(除)運算。
計算機中機器數(shù)的字長往往是固定的,當機器數(shù)左移n位或右移n位時,必然會使其n位低位或n位高位出現(xiàn)空位。那么,對空出的空位應(yīng)該添補0還是1呢?
這與機器數(shù)采用有符號數(shù)還是無符號數(shù)有關(guān),對有符號的移位叫算術(shù)移位。
下表列出了三種不同碼制的機器數(shù)(整數(shù)或小數(shù)均可),分別對應(yīng)正數(shù)或負數(shù),移位后的添補規(guī)則。
必須注意的是:不論是正數(shù)還是負數(shù),移位后其符號位均不變,這是算術(shù)移位的重要特點。
乍看可能覺得很唬人,其實你只要會計算原碼、反碼、補碼就行,把原碼移位后,計算出對應(yīng)的反碼和補碼就行;謹記算數(shù)移位符號位不變!!!
例題:
解:
(1)當A=+26=+11010(二進制碼),機器數(shù)字長為8位,則 [A]原=[A]補=[A]反=0,0011010
移位結(jié)果表示如下:
可見,對于正數(shù),三種機器數(shù)移位后符號位不變,左移時最高數(shù)位丟1,結(jié)果出錯;右移時最低數(shù)位丟1,影響精度。
(2)當 A=-26=-11010(二進制碼),三種機器數(shù)移位結(jié)果示于下表
原碼
反碼
補碼
下圖示意了機器中實現(xiàn)算術(shù)左移和右移操作的硬件框圖
有符號數(shù)的移位稱為算術(shù)移位,無符號數(shù)的移位稱為邏輯移位。
邏輯移位的規(guī)則是:
符號位跟著一起移
算術(shù)移位符號位不動
例如
顯然,兩種移位的結(jié)果是不同的。
上例中為了避免算術(shù)左移時最高數(shù)位丟1,可采用帶進位(Cy)的移位,其示意圖如下圖所示。算術(shù)左移時,符號位移至Cy,最高數(shù)位就可避免移出。
減法運算是計算機中最基本的運算,因減法運算可看作被減數(shù)加上一個減數(shù)的負值,即A-B=A+(-B),故在此將機器中的減法運算和加法運算合在一起討論。
現(xiàn)代計算機中都采用補碼作加減法運算。
補碼加法的基本公式為:
即補碼表示兩個數(shù)在進行加法運算時,可以把符號位與數(shù)位同等處理,只要結(jié)果不超出機器能表示的數(shù)值范圍,運算后的結(jié)果按2^(n+1)取模(對于整數(shù));或按2取模(對于小數(shù)),就能得到本次加法的運算結(jié)果。
對于減法,因 A-B=A+(-B),則 [A-B]補=[A+(-B)]補
由補碼加法基本公式可得:
因此,若機器數(shù)采用補碼, 當求A-B時, 只需先求[-B]補(稱[-B]補為“求補”后的減數(shù)),就可按補碼加法規(guī)則進行運算。而[-B]補由[B]補連同符號位在內(nèi),每位取反,末位加1而得。
例:A=0.1010,B=-0.0101,用補碼的加法求A+B
解:
[A]補=0.1011,[B]補=1.1011
[A]補+[B]補=0.1011+1.1011=0.0110(按模2的意義,最左邊的1丟掉),所以 A+B=0.0110
例:x=0.1001,y=-0.0011,用補碼的減法求x-y
解:
[x]補=0.1001,[y]補=1.1101,[-y]補=0.0011
[x]補-[y]補=[x]補+[-y]補=0.1001+0.0011=0.1100,所以 x-y=0.1100
例:設(shè)機器數(shù)字長為8位,其中一位為符號位,令A(yù)=-93,B=+45,求[A-B]補。
解:
由A=-93=-1011101,得[A]補=1,0100011,由B=+45=+0101101,得[B]補=0,0101101,[-B]補=1,1010011
[A-B]補=[A]補+[-B]補=1,0100011+1,1010011=10,1110110
按模2^(n+1)的意義,最左邊的“1”自然丟掉,故[A-B]補=0,1110110,還原成真值得A-B=118,結(jié)果出錯,這是因為A-B=-138超出了機器字長所能表示的范圍。在計算機中,這種超出機器字長的現(xiàn)象,叫溢出。為此,在補碼定點加減運算過程中,必須對結(jié)果是否溢出作出明確的判斷。
因此在判斷溢出時可以根據(jù)參加運算的兩個數(shù)據(jù)和結(jié)果的符號位進行。
兩個符號位相同的補碼相加,如果和的符號位與加數(shù)的符號相反,則表明運算結(jié)果溢出;
兩個符號位相反的補碼相減,如果差的符號位與被減數(shù)的符號位相反,則表明運算結(jié)果溢出。
原理:
和的符號位發(fā)生改變肯定是數(shù)值部分進位上來的
或者再想想,兩個負數(shù)相加的和,它的符號位發(fā)生改變豈不是正數(shù)
負數(shù) + 負數(shù)=正數(shù)? 我趣里的
這種方法需要判斷操作是加法還是減法,以及運算結(jié)果與操作數(shù)的符號關(guān)系。
利用數(shù)據(jù)編碼的最高位(符號位)和次高位(數(shù)值部分的最高位)的進位狀況來判斷運算結(jié)果是否發(fā)生了溢出。
兩個補碼數(shù)實現(xiàn)加減運算時,若最高數(shù)值位向符號位的進位值與符號位產(chǎn)生的進位輸出值不相同,則表明加減運算產(chǎn)生了溢出
因為當x和y均為n+1位正整數(shù)時,其和有兩種情況:
當x和y都是n+1位負數(shù)時,其和也有兩種情況:
例:設(shè)x=+1011, y=+1001,求[x+y]補。
解:
[x]補=01011, [y]補=01001
[x+y]補=01011+01001=10100
兩個正數(shù)相加,最高兩位的進位為01,表示發(fā)生了溢出,其結(jié)果為負數(shù),顯然是錯誤的。
例:設(shè)x=-1101,y=-1011,求[x+y]補。
解:
[x]補=10011, [y]補=10101
[x+y]補=10011+10101=01000
兩個負數(shù)相加,最高兩位的進位為10,表示發(fā)生了溢出,其結(jié)果為正數(shù),顯然是錯誤的。
正常時兩個符號位的值相同,在運算結(jié)果中當兩個符號位不同時則表明發(fā)生了溢出。
運算結(jié)果的符號位為01表明兩個正數(shù)相加,結(jié)果大于機器所能表示的最大正數(shù),稱為上溢;
運算結(jié)果的符號位為10表明兩個負數(shù)相加,結(jié)果小于機器所能表示的最小負數(shù),稱為下溢。
也就是說,兩個正數(shù)相加,數(shù)值位不應(yīng)向符號位同時產(chǎn)生進位,使得結(jié)果數(shù)的符號位和操作數(shù)的一樣,為00;當運算結(jié)果的兩個符號位不相同時,表明出現(xiàn)了溢出。
例:設(shè)x=+1100,y=+1000,求6位雙符號位補碼之和[x+y]補。
解:
[x]補=001100, [y]補=001000
[x+y]補=001100+001000=010100
[x+y]補=010100,其中兩個符號位出現(xiàn)01,表示已溢出。且第1個符號位為0,上溢
例:設(shè)x=-1100,y=-1000,求6位雙符號位補碼之和[x+y]補。
解:
[x]補=110100, [y]補=111000
[x+y]補=110100+111000=101100
[x+y]補=101100,其中兩個符號位出現(xiàn)10,表示已溢出。且第1個符號位為1,下溢
從上述例子中還看出,不論溢出與否,最高位始終指示正確的符號。
采用雙符號位補碼后,任何小于1的正數(shù),兩個符號位都是0;任何大于-1的負數(shù),兩個符號位都是1。
如果兩個數(shù)相加后,其結(jié)果的符號位出現(xiàn)01或10時,表示發(fā)生溢出。因為兩個絕對值小于1的數(shù)相加,其結(jié)果不會大于或等于2,所以最高位總是表示正確的符號。這也可以表示為:
當最高數(shù)據(jù)位有進位而符號位無進位時產(chǎn)生上溢出;當最高數(shù)據(jù)位無進位而符號位有進位時,表示下溢出。
在雙符號位補碼中,正常的數(shù)據(jù)中兩個符號位總是相同的,所以在存儲數(shù)據(jù)時不必重復(fù)存儲,只是在將數(shù)據(jù)送往運算部件進行運算時才把符號位進行復(fù)制形成雙符號位補碼。
在計算機中,乘法運算是一種很重要的運算,有的機器由硬件乘法器直接完成乘法運算,有的機器內(nèi)沒有乘法器,但可以按機器作乘法運算的方法,用軟件編程實現(xiàn)。因此,學習乘法運算方法不僅有助于乘法器的設(shè)計,也有助于乘法編程。
下面從分析筆算乘法入手,介紹機器中用到的幾種乘法運算方法。
設(shè)A=0.1101,B=0.1011,求A×B。
筆算乘法時乘積的符號由兩數(shù)符號心算而得:正正得正;其數(shù)值部分的運算如下:
所以 A×B=+0.10001111
可見,這里包含著被乘數(shù)4的多次左移,以及四個位積的相加運算。
若計算機完全模仿筆算乘法步驟,將會有兩大困難:
為此,對筆算乘法進行改進
將A?B=A?0.1011
=0.1A+0.001?A+0.0001?A
=0.1A+0.00?A+0.001(A+0.1A)
=0.1A+0.01[0?A+0.1(A+0.1A)]
=0.1{A+0.1[0?A+0.1(A+0.1A)]}
=2^-1{A+2^-1 [0?A+2^-1 (A+2^-1A)]}
=2^-1{A+2^-1 [0?A+2^-1 (A+2^-1(A+0))]}
由上式可見,兩數(shù)相乘的過程,可視作加法和移位(乘2^-1相當于做一位右移)兩種運算,這對計算機來說是非常容易實現(xiàn)的。
從初始值為0開始,對上式作分步運算,則:
上述運算過程可歸納為:
乘法運算可用移位和加法來實現(xiàn),當兩個四位數(shù)相乘,總共需做四次加法和四次移位。
由乘數(shù)的末位值確定被乘數(shù)是否與原部分積相加,然后右移一位,形成新的部分積;同時,乘數(shù)也右移一位,由次低位作新的末位,空出最高位放部分積的最低位。
每次做加法時,被乘數(shù)僅僅與原部分積的高位相加,其低位被移至乘數(shù)所空出的高位位置。
計算機很容易實現(xiàn)這種運算規(guī)則。用一個寄存器存放被乘數(shù),一個寄存器存放乘積的高位,又用一個寄存器存放乘數(shù)及乘積的低位,再配上加法器及其他相應(yīng)電路,就可組成乘法器。又因加法只在部分積的高位進行,故不但節(jié)省了器材,而且還縮短了運算時間。
由于原碼表示與真值極為相似,只差一個符號,而乘積的符號又可通過兩數(shù)符號的邏輯異或求得,因此,上述討論的結(jié)果可以直接用于原碼一位乘,只需加上符號位處理即可。
遞推公式:
例:x=-0.1110 y=0.1101 求【x.y】原
即【x.y】原=0.10110110
這邊直接介紹加減交替法
小數(shù)定點除法對被除數(shù)和除數(shù)有一定的約束:
0 < 被除數(shù) <=除數(shù)
加減交替法又稱不恢復(fù)余數(shù)法,可以認為它是恢復(fù)余數(shù)法的一種改進算法。
分析原碼恢復(fù)余數(shù)法得知:
可見,原碼恢復(fù)余數(shù)法可歸納為:
這里已看不出余數(shù)的恢復(fù)問題了,而只是做加y*或減y*,因此,一般把它叫做加減交替法或不恢復(fù)余數(shù)法。
例:已知:x=-0.1011,y=-0.1101,求:[x÷ y]原
解:
[x]原=1.1011, x*=0.1011
[y]原=0.1101,y*=0.1101,[-y*]補=1.0011
商值的求解過程如下表所示:
商的符號位為
所以
分析此例可見,n位小數(shù)的除法共上商n+1次,第一次商用來判斷是否溢出。
倘若比例因子選擇恰當,除數(shù)結(jié)果不溢出,則第一次商肯定是0。如果省去這位商,只需上商n次即可,此時除法運算一開始應(yīng)將被除數(shù)左移一位減去除數(shù),然后再根據(jù)余數(shù)上商。
大概過程:
除了用原碼計算,還可以用補碼進行計算,計算出來的是補碼,我們只需掌握原碼計算法,把得到的結(jié)果轉(zhuǎn)為補碼就行了,這里就不再細說了
首先給出浮點數(shù)加減法運算步驟:
類比平常我們用到的帶階數(shù)的加減法,我們常常會把兩個數(shù)的階數(shù)轉(zhuǎn)化為一致,再針對尾數(shù)進行加減法運算,計算機中也是一樣。
對以上兩個浮點數(shù),我們進行對階,首先求他們的階差
在對階的過程中有兩種方式,大階向小階看齊,小階向大階看齊。
但是在計算機中,因為存儲空間的字長限制,如果我們使用大階向小階看齊,需要把數(shù)字向左移,可能把數(shù)字的最高位給移除掉,從而使整個數(shù)據(jù)出現(xiàn)錯誤;
如果我們使用小階向大階看齊,需要把數(shù)字向右移,有可能丟失數(shù)據(jù)的精度,但不會使整個數(shù)據(jù)出現(xiàn)錯誤;
因此我們對階的原則是:小階向大階看齊
求 X + Y
可以看到這邊我們也用到了上文提到的雙符號
判斷一個數(shù)是否是規(guī)格化數(shù),計算機中可以通過異或電路,比較一個數(shù)補碼的符號位跟第一位數(shù)是否相同來實現(xiàn)。
概括一下:
對于雙符號位的補碼規(guī)格化形式位:00.1xxxxxxxx;11.0xxxxxxxx
尾數(shù)左移一位(大小變?yōu)樵瓉淼膬杀叮A碼減 1 ,直到數(shù)符和第一數(shù)位不同為止
計算 X + Y的結(jié)果為: [ x + y ] 補=00, 11; 11. 1001
結(jié)果為非規(guī)格化數(shù),左規(guī)后
注意這邊0.1110是原碼
當 尾數(shù)溢出 ( >1 )時,需 右規(guī), 即尾數(shù)出現(xiàn) 01. ××…×或 10. ××…×時, 尾數(shù)右移一位,階碼加 1
總結(jié):
當尾數(shù)出現(xiàn)00.0xxxxxxx、11.1xxxxxxxxx 需要左規(guī)
當尾數(shù)出現(xiàn)01.xxxxxxxx、10.xxxxxxxxxx 需要右規(guī)
無論左規(guī)還是右規(guī)對尾數(shù)進行移位是都是邏輯移位(即符號位也參與移位)
例:
求x+y(除階符、數(shù)符外,階碼取3位,尾數(shù)取6位)
通過雙符號位,我們可以知道尾數(shù)相加后,尾數(shù)溢出(且為上溢),因此我們需要進行右規(guī)
注意這邊0.100101是原碼
執(zhí)行右規(guī)或者對階時,有可能會在尾數(shù)低位上增加一些值,最后需要把它們移掉。(進行尾數(shù)加減時不要把對階過程在尾數(shù)低位上增加的值去掉,不是不去掉,而是在舍入這一步去掉。)比如說,原來參與運算的兩個數(shù)(加數(shù)和被加數(shù))算上符號位一共有6個數(shù),通過一系列操作后運算結(jié)果變成了8個數(shù),這時需要把第7和8位的數(shù)去掉。如果直接去掉,會使精度受影響,通常有下邊兩個方法:
比如:X=00.11010111,假設(shè)原本加數(shù)和被加數(shù)算上符號位一共有6個數(shù),結(jié)果X是10個數(shù),那么要去掉后四個數(shù)(0111)。由于0111首位是0(即要去掉的數(shù)的最高位為0),這種情況下,直接去掉這四個數(shù)就可以。所以最后結(jié)果為 X=00.1101
結(jié)果 X=00.11001001,這時要去掉的數(shù)為1001四個數(shù),由于這四個數(shù)的首位為1(即要去掉的數(shù)的最高位為1),這種情況下,直接去掉這四個數(shù),再在去掉這四個數(shù)的新尾數(shù)的末尾加1。如果+1后又出現(xiàn)了溢出(即符號位為:01或10),繼續(xù)進行右規(guī)操作。所以最后結(jié)果為 X=00.1101。
這個比較簡單,去掉多余的尾數(shù),然后保證去掉這四個數(shù)的新尾數(shù)的最后一位為1(即是1不用管,是0改成1)即可。比如 Z=00.11000111,置1法之后的結(jié)果為Z=00.11001。
階碼溢出在規(guī)格化和右移的過程中都有可能發(fā)生,
例如:
設(shè)機器數(shù)為補碼,尾數(shù)為 規(guī)格化形式, 并假 設(shè)階符取 2 位,階碼的數(shù)值部分取 7 位,數(shù)符取 2 位,尾數(shù)取 n 位,則該 補碼 在數(shù)軸上的表示為
Arithmetic Logic Unit
電路圖
ALU是一個組合邏輯電路,所謂組合邏輯電路指電路沒有記憶功能,所以Ai,Bi 存儲在寄存器中
要求n位加法器的進位同時產(chǎn)生,分為單重分組跳躍進位鏈和雙重分組體跳躍進位鏈
n位全加器分為若干小組,小組中的進位同時產(chǎn)生,小組和小組之間采用串行進位
以 n=16 為例
n位全加器分為若干大組,大組中又包含若干小組。每個大組中小組的最高位進位同時產(chǎn)生。大組與大組之間采用串行進位
以 n=32 為例
分為2大組,每個大組有4個小組,每個小組有4位數(shù)據(jù)
第2大組進位圖(第1大組進位圖和這個一樣)
C15 會傳到第1大組中
完整圖
邊有很多需要用Excel的朋友,做財務(wù)的、做人事的、做銷售的。有的小伙伴經(jīng)常抱怨說:作為“表哥”、“表姐”的他們,天天和數(shù)據(jù)打交道 ,昏天黑地處理數(shù)據(jù),不是在統(tǒng)計數(shù)據(jù)就是在統(tǒng)計數(shù)據(jù)的路上。他們也經(jīng)常自嘲:要是你聯(lián)系不上我,就說明我深深地陷在統(tǒng)計的腦海里。
其實,如果你熟練地掌握一些統(tǒng)計類函數(shù),就不用那么辛苦了。今天我們就來說說常見的統(tǒng)計函數(shù)應(yīng)用技巧。
統(tǒng)計類函數(shù)是Excel中使用頻率最高的函數(shù),絕大多數(shù)報表都離不開它們,從簡單的計數(shù)與求和,到多區(qū)域中多種條件下的計數(shù)與求和,此類函數(shù)總是能幫助我們解決很多大問題。
根據(jù)函數(shù)的功能,主要可將統(tǒng)計函數(shù)分為數(shù)理統(tǒng)計函數(shù)、分布趨勢函數(shù)、線性擬合和預(yù)測函數(shù)、假設(shè)檢驗函數(shù)和排位函數(shù)。
由于篇幅有限,今天我們主要來看一看最常用和最有代表性的一些函數(shù)。
COUNTA函數(shù)用于計算區(qū)域中所有不為空的單元格個數(shù)。
語法結(jié)構(gòu):COUNTA (value1,[value2],...)
參數(shù):
value1:必需參數(shù),表示要計數(shù)的第一個參數(shù)。
value2,...:可選參數(shù),表示要計數(shù)的其他參數(shù),最多可包含255 個參數(shù)。
案例:
要在員工獎金表中統(tǒng)計出獲獎人數(shù),因為沒有獎金人員對應(yīng)的單元格為空,有獎金人員對應(yīng)的單元格為獲得的具體獎金額,所以可以通過COUNTA函數(shù)統(tǒng)計相應(yīng)列中的非空單元格個數(shù)來得到獲獎人數(shù),具體操作步驟如下:
打開素材文件\員工獎金表.xlsx,在A21單元格中輸入相應(yīng)的文本,在B21單元格中輸入公式【=COUNTA(D2:D19)】,返回結(jié)果為【14】,即統(tǒng)計到該單元格區(qū)域中有14個單元格非空,也就是說有14人獲獎。
COUNTBLANK函數(shù)用于計算指定單元格區(qū)域中空白單元格的個數(shù)。
語法結(jié)構(gòu):COUNTBLANK(range)
參數(shù):
range:必需參數(shù),表示需要計算其中空白單元格個數(shù)的區(qū)域。
案例:要在上例中統(tǒng)計出沒有獲獎的人數(shù),除了可以使用減法從總?cè)藬?shù)中減去獲獎人數(shù)外,還可以使用COUNTBLANK函數(shù)進行統(tǒng)計,具體操作步驟如下:
在A22單元格中輸入相應(yīng)的文本,在B22單元格中輸入公式【=COUNTBLANK(D2:D19)】,返回結(jié)果為【4】,即統(tǒng)計到該單元格區(qū)域中有4個空單元格,也就是說有4人沒有獎金。
COUNTIF函數(shù)用于對單元格區(qū)域中滿足單個指定條件的單元格進行計數(shù)。
語法結(jié)構(gòu):COUNTIF (range,criteria),也可以簡單理解為COUNTIF (條件區(qū)域,條件 )
參數(shù):
range:必需參數(shù),表示要對其進行計數(shù)的一個或多個單元格,其中包括數(shù)字或名稱、數(shù)組或包含數(shù)字的引用。空值和文本值將被忽略。
criteria:必需參數(shù),表示統(tǒng)計的條件,可以是數(shù)字、表達式、單元格引用或文本字符串。
案例:在招聘統(tǒng)計表中要對各招聘渠道參加面試的人數(shù)進行統(tǒng)計,就需要使用 COUNTIF 函數(shù),具體操作步驟如下:
Step01 引用其他工作表。打開素材文件\招聘統(tǒng)計表.xlsx,在【招聘渠道統(tǒng)計表】工作表中的B2單元格中輸入公式【=COUNTIF()】,將光標定位到公式括號中,單擊【應(yīng)聘人員信息表】工作表標簽。
Step02 選擇引用范圍。切換到【應(yīng)聘人員信息表】工作表中,拖動鼠標選擇B2:B135單元格區(qū)域,在公式中輸入【,】。
Step03 完成公式的輸入。切換到【招聘渠道統(tǒng)計表】工作表中,選擇A2單元格,完成公式【=COUNTIF( 應(yīng)聘人員信息表 !B2:B135, 招聘渠道統(tǒng)計表 !A2)】的輸入。
Step04 計算出面試人員的人數(shù)。按【Enter】鍵計算出招聘網(wǎng)站1渠道參加面試的人員數(shù)量,向下復(fù)制公式,計算出其他招聘渠道參加面試的人數(shù)。
COUNTIFS函數(shù)用于計算單元格區(qū)域中滿足多個條件的單元格數(shù)量。
語法結(jié)構(gòu):COUNTIFS(criteria_range1,criteria1,[criteria_range2, criteria2],...), 也可以簡單理解為COUNTIFS ( 條件匹配查詢區(qū)域1, 條件1, 條件匹配查詢區(qū)域2, 條件2, 依此類推)
參數(shù):
criteria_range1:必需參數(shù),在其中計算關(guān)聯(lián)條件的第一個區(qū)域。
criteria1:必需參數(shù),條件的形式為數(shù)字、表達式、單元格引用或文本,可用來定義將對哪些單元格進行計數(shù)。
criteria_range2,criteria2,...:可選參數(shù),附加的區(qū)域及其關(guān)聯(lián)條件,最多允許127個區(qū)域 /條件對。
案例:繼續(xù)上例的操作,使用COUNTIFS函數(shù)對各渠道錄用的人數(shù)和實際到崗人數(shù)進行統(tǒng)計,具體操作步驟如下:
Step01 在C2單元格中輸入公式【=COUNTIFS(應(yīng)聘人員信息表!$B:$B5,A2, 應(yīng)聘人員信息表 !$H: $H5,">0")】,按【Enter】鍵計算出招聘網(wǎng)站1錄用的人數(shù)。
Step02 在D2單元格中輸入公式【=COUNTIFS( 應(yīng)聘人員信息表 !$B:$B5,A2, 應(yīng)聘人員信息表 !$I: $I5,">0")】, 按【Enter】 鍵計算出招聘網(wǎng)站1實際到崗人數(shù)。
Step03 使用Excel自動填充功能,復(fù)制C2和D2單元格中的公式,計算出其他招聘渠道的錄用人數(shù)和實際到崗人數(shù)。
AVERAGEA函數(shù)與AVERAGE函數(shù)的功能類似,都是計算數(shù)值的平均值, 只是AVERAGE函 數(shù)計算包含數(shù)值單元格的平均值,而AVERAGEA 函數(shù)則用于計算參數(shù)列表中所有非空單元格的平均值(即算術(shù)平均值)。
語法結(jié)構(gòu):AVERAGEA (value1,[value2],...)
參數(shù):
value1:必需參數(shù),表示需要計算平均值的第一個單元格、單元格區(qū)域或值。
value2,...:可選參數(shù),表示計算平均值的第2~255個單元格、單元格區(qū)域或值。
案例:要在員工獎金表中統(tǒng)計出該公司員工領(lǐng)取獎金的平均值,可以使用AVERAGE和AVERAGEA函數(shù)進行兩種不同方式的計算,具體操作步驟如下:
Step01 計算所有員工的獎金平均值。打開文件 \ 員工獎金表.xlsx,復(fù)制Sheet1工作表,在D列中數(shù)據(jù)區(qū)域部分的空白單元格中輸入非數(shù)值型數(shù)據(jù),這里輸入文本型數(shù)據(jù)【無】,在A21單元格中輸入文本【所有員工的獎金平均值:】,在C21單元格中輸入公式【=AVERAGEA(D2:D19)】, 計 算出所有員工的獎金平均值約為287。
Step02 計算所有獲獎員工的獎金平均值。在A22單元格中輸入文本【所有獲獎員工的獎金平均值:】,在C22單元格中輸入公式【=AVERAGE(D2:D19)】,計算出所有獲獎員工的獎金平均值為369。