計算機中的數(shù)與數(shù)據(jù)

前言

最近在看《Computer System: A Programmer's Perspective》纸兔,學(xué)會了很多基礎(chǔ)性的知識妒峦,于是總結(jié)出來與大家分享幅疼。

位與二進(jìn)制

在現(xiàn)實生活中背捌,我們會用紙和筆來記錄數(shù)據(jù)癌刽,比如在之前智能手機還沒有普及的年代力图,還有相當(dāng)一部分人使用小本本來記錄電話號碼步绸,顯然電話號碼作為一種數(shù)據(jù),記錄在紙上搪哪。

那么在計算機中是如何記錄數(shù)據(jù)靡努,表達(dá)信息的呢坪圾?

計算機使用一個序列的位(bit)來記錄數(shù)據(jù)。

一個位中存儲著一個二進(jìn)制數(shù)字惑朦,什么是二進(jìn)制呢兽泄?二進(jìn)制簡單來說就是逢二進(jìn)位的一種進(jìn)制,人類最常用漾月,最直觀的一直使用著十進(jìn)制病梢,可用的符號為:0,1梁肿,2蜓陌,3,4吩蔑,5钮热,6,7烛芬,8隧期,9,總共有10個符號赘娄,二進(jìn)制則只需要兩個符號0和1仆潮。對機器來說,使用二進(jìn)制是非常方便的一種形式遣臼,因為二進(jìn)制只需要兩種符號性置,也就是說,機器只需要能夠維持兩種不同的狀態(tài)來對應(yīng)這兩種符號即可揍堰,比如高電壓和低電壓鹏浅,通電和斷電等等。所以屏歹,對計算機來說篡石,使用二進(jìn)制(維持兩個狀態(tài))是非常有效的方式。

計算機使用一連串的位來記錄數(shù)據(jù)西采,比如1位,那么這個位上只能表示0或1继控,通常1位的數(shù)據(jù)幾乎沒有什么作用械馆。在現(xiàn)在的計算機中,使用8個位來作為一個基本的單位武通,稱為字節(jié)(byte)霹崎,那么它寫出來應(yīng)該是這樣的:

//8個位都是0,對應(yīng)十進(jìn)制數(shù)字0冶忱,中間空開一個空格尾菇,四位四位的寫在一起是為了可讀性
0000 0000   

//右側(cè)的一般稱為最低位,左側(cè)的稱為最高位,最低位加1派诬,對應(yīng)十進(jìn)制中的1
0000 0001   

//最低位繼續(xù)加1劳淆,1+1=2,因為是二進(jìn)制默赂,必須進(jìn)位了沛鸵,對應(yīng)十進(jìn)制數(shù)字2
0000 0010
0000 0011

...

//8位二進(jìn)制最大值,對應(yīng)十進(jìn)制2^8-1=255
1111 1111 

以上就是一串從0開始遞增的二進(jìn)制序列缆八。

十六進(jìn)制

剛剛說完了二進(jìn)制數(shù)曲掰,現(xiàn)在簡單介紹一下十六進(jìn)制數(shù),二進(jìn)制數(shù)與十六進(jìn)制數(shù)之間有非常巧妙的關(guān)系奈辰。

十進(jìn)制需要10個符號:0, 1, 2, 3, 4, 5, 6, 7, 8, 9
二進(jìn)制需要2個符號:0, 1
十六進(jìn)制需要16個符號:0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f
其中栏妖,a-f分別對應(yīng)著十進(jìn)制中的10, 11, 12, 13, 14, 15

十六進(jìn)制數(shù)值中的英文字母是不區(qū)分大小寫的

現(xiàn)在奖恰,讓我們來考慮4位二進(jìn)制數(shù)

0000(2) -> 0(10) -> 0(16)   //括號中表示進(jìn)制

一個4位的二進(jìn)制數(shù)最小為0000吊趾,也就是十進(jìn)制中的0,也是十六進(jìn)制的0房官。那么4位二進(jìn)制最大的值為1111趾徽,那么它的值為


也就是說,四位二進(jìn)制能表示數(shù)的范圍區(qū)間為[0, 15]翰守,這剛好是1位16進(jìn)制數(shù)所能表示的數(shù)值范圍孵奶。

四位二進(jìn)制數(shù)能搞好使用一位十六進(jìn)制數(shù)來表示

于是蜡峰,當(dāng)計算機中的數(shù)值使用二進(jìn)制表示時了袁,通常會出現(xiàn)成片成片的010101……,這看起來非常頭疼湿颅,非常容易讓人看錯载绿,但是,我們可以從低位開始油航,四個四個的表示為十六進(jìn)制數(shù)崭庸。如下所示:

0000 0000(2) -> 00(16)
0000 1111(2) -> 0x0f    //通常,"0x"前綴用來指明是一個十六進(jìn)制數(shù)
1111 0001(2) -> 0xF1 //十六進(jìn)制的字母是不區(qū)分大小寫的谊囚,注意這里大寫的F
 100 1111(2) -> 0x4f //注意怕享!這里的二進(jìn)制數(shù)只有7位,通常我們從最低位開始轉(zhuǎn)換成十六進(jìn)制數(shù)镰踏,高位在沒有指明的情況下函筋,使用0補齊
0100 1111(2) -> 0x4f //上一行的二進(jìn)制數(shù)高位補齊0的情況

有了十六進(jìn)制,我們就可以方便簡潔的表示非常多位的二進(jìn)制數(shù)奠伪,有了更高的可讀性跌帐。

整數(shù)

整數(shù)又分為有符號與無符號的區(qū)別首懈,無符號整數(shù)即是大于等于0的整數(shù)

無符號整數(shù)的表示

無符號整數(shù)的表示是基于最原始的二進(jìn)制表示數(shù)據(jù)的辦法谨敛,也就是說究履,給定n位二進(jìn)制序列,它所能表示的數(shù)值范圍是[0, 2^n - 1]佣盒。

2^n-1是怎么來的呢挎袜,其實很簡單,比如我們給出一個字節(jié)(8位)來表示一個整數(shù)肥惭,0000 0000盯仪,它能表示的最小值應(yīng)該是0了,也就是8位全是0蜜葱,那么最大值呢全景?當(dāng)然是8位全是1了,也就是1111 1111牵囤,現(xiàn)在不妨給它加個1爸黄,那么它會變成9位的二進(jìn)制數(shù)值1 0000 000,此時揭鳞,這個9位的二進(jìn)制數(shù)的值為2^8 = 256炕贵,那么8位最大值當(dāng)然就是2^8-1=255了。

所以野崇,無符號整數(shù)在計算機中的表示称开,就是簡單的對位序列的數(shù)值理解即可。

有符號整數(shù)的表示

通過位序列來表示有符號整數(shù)是非常巧妙的乓梨,稱之為補碼編碼的方式來表示有符號整數(shù)鳖轰。所謂使用補碼來表示有符號數(shù),實際上對二進(jìn)制序列并沒有什么特別大的處理扶镀,它仍然是010101……這樣的一串東西蕴侣,只是我們用一套稱為“補碼”的規(guī)則來理解這串位序列而已。

補碼就是將位序列的最高位解釋為負(fù)權(quán)臭觉。

比如昆雀,給定位序列,一個字節(jié)1000 0001蝠筑,如果它是表示無符號整數(shù)忆肾,它的值是多少呢?很簡單1 \times 2^7 + 1 = 129菱肖,那么如果我們用補碼來理解它呢?最高位是負(fù)權(quán)旭从,也就是-1 \times 2^7 + 1 = -127稳强。

補碼會將最高位理解為一個負(fù)數(shù)场仲,直觀點來看,就是當(dāng)最高位是1的時候退疫,是一個非常大的負(fù)數(shù)加上后面的正整數(shù)渠缕,后面的正整數(shù)越大,這個負(fù)數(shù)越小褒繁,越靠近0亦鳞,當(dāng)最高位是0的時候,那么負(fù)權(quán)整個都是0棒坏,剩下的幾位如同無符號整數(shù)一樣表示正整數(shù)燕差。

我們用一串遞增的序列來理解。

0000 0000 -> -0 + 0 = 0
0000 0001 -> -0 + 1 = 1
...
0111 1110 -> -0 + 126 = 126
0111 1111 -> -0 + 127 = 127
1000 0000 -> -128 + 0 = -128
1000 0001 -> -128 + 1 = -127
1000 0010 -> -128 + 2 = -126
...
1111 1110 -> -128 + 126 = -2
1111 1111 -> -128 + 127 = -1

通過上述遞增的序列坝冕,你會發(fā)現(xiàn)徒探,8位二進(jìn)制能表示的有符號整數(shù)的范圍是[-128, 127],我們可以用數(shù)學(xué)的語言來描述一個n位的二進(jìn)制能表示的有符號整數(shù)范圍是[-2^{n-1},2^{n-1}-1]喂窟。

有符號數(shù)與無符號數(shù)的相互轉(zhuǎn)換

通常在高級程序語言中测暗,有無符號整數(shù)的相互轉(zhuǎn)換是不改變位序列的,只是換了一種“解析”方式去解釋位序列磨澡,比如說1000 0000是一個無符號數(shù)碗啄,那么它的值是128,如果我們把它轉(zhuǎn)換成一個有符號數(shù)稳摄,位序列不變稚字,只是用補碼的方式去理解它,那么它的數(shù)值就變成了-128了秩命。

我來用一張圖片說明得更清楚一些尉共。

8-bit有符號數(shù)與無符號數(shù)相互轉(zhuǎn)換

如上圖所示,在數(shù)據(jù)大小為8位的情況下弃锐,它的低7位所表示的數(shù)值范圍
[0,127]
之間都是可以做到安全的轉(zhuǎn)換袄友,但是當(dāng)最高位不為0的時候,有符號數(shù)與無符號數(shù)的相互轉(zhuǎn)換就會變成不安全的霹菊。

在我們寫代碼的時候一定要注意這一點剧蚣。

擴展一個數(shù)值的位

如果我們要將一個8位的數(shù)據(jù)放到16位的容器中去,那么我們需要對數(shù)據(jù)進(jìn)行擴展旋廷, 也就是我們需要確定新的高8位應(yīng)該是放0還是放1鸠按。

這個問題很簡單,對于無符號數(shù)的擴展饶碘,只要簡單的在高位上補充滿0即可目尖,這種方式稱為零擴展。對于有符號數(shù)扎运,只需要在高位補充滿1即可瑟曲,這種方式稱為符號擴展饮戳。

整數(shù)的加法計算

整數(shù)的計算分為,無符號整數(shù)加法洞拨,有符號整數(shù)加法扯罐,無符號與有符號整數(shù)混合的加法。

對于無符號整數(shù)的計算烦衣,只是單單從位級上考慮就行了歹河,但是當(dāng)兩個二進(jìn)制數(shù)相加后,最高位向前進(jìn)1了花吟,那么就會產(chǎn)生溢出秸歧,本來應(yīng)該變成一個更大的數(shù),結(jié)果卻變小了示辈。

對于有符號數(shù)的計算寥茫,同樣的也是從位級上進(jìn)行加法計算,一樣的矾麻,最高位如果向前進(jìn)一了纱耻,一樣會產(chǎn)生溢出。比如對于8位的數(shù)據(jù)险耀,-128 - 128 -> 0弄喘,我們在位級上進(jìn)行考慮

//這是一個豎式
1000 0000
1000 0000
----------
0000 0000

對于有符號數(shù)與無符號數(shù)混合的表達(dá)式,一般需要查看編譯器是如何處理這個問題的甩牺,有可能是將有符號數(shù)轉(zhuǎn)成無符號數(shù)再進(jìn)行計算蘑志,也有可能是將無符號數(shù)轉(zhuǎn)成有符號數(shù)再進(jìn)行計算。這個問題跟整型與浮點數(shù)相加是類似的問題贬派,還是看編譯器/虛擬機是具體如何解決這個問題的急但。

浮點數(shù)

前面所說的有符號整數(shù)與無符號整數(shù),都屬于定點數(shù)搞乏,就是小數(shù)點固定的數(shù)波桩,而浮點數(shù),即小數(shù)點是可以浮動(變化)的數(shù)请敦。自從我聽到浮點數(shù)這個概念镐躲,在一個很長的時間里,我都以為浮點數(shù)就是指小數(shù)侍筛,其實不是這樣的萤皂,浮點數(shù)并不是狹隘的說一定要表示為小數(shù)(如123.45),應(yīng)該更準(zhǔn)確的理解為小數(shù)點的位置不是固定的匣椰,也就是說裆熙,這種數(shù)的“表示/解析”方法是可以表示小數(shù)點在不同位置的數(shù)的。

二進(jìn)制小數(shù)

在解釋浮點數(shù)之前,我們先要知道一下二進(jìn)制的小數(shù)是什么情況齐媒。
先看一個二進(jìn)制整數(shù)的例子,如101纷跛,那么它的十進(jìn)制數(shù)值為1 \times 2^2 + 1 \times 2^0 = 5,那么邀杏,當(dāng)二進(jìn)制數(shù)值有小數(shù)點時贫奠,101.101,它的十進(jìn)制數(shù)值為
1 \times 2^2 + 1 \times 2^0 + 1 \times 2^{-1} + 1 \times 2^{-3}= 4 + 1 + \frac{1}{2} + \frac{1}{8} = 5\frac{5}{8}

可以發(fā)現(xiàn)望蜡,0.1(2)唤崭,0.01(2),0.001(2)……二進(jìn)制小數(shù)每一位能表示為\frac{1}{2}脖律,\frac{1}{4}谢肾,\frac{1}{8}……因此它要表示某一個十進(jìn)制的小數(shù),需要用這些部分累加在一起實現(xiàn)小泉。

IEEE浮點標(biāo)準(zhǔn)

在生產(chǎn)環(huán)境中芦疏,一個基本的浮點數(shù)都是32位的,不會像上文中一直使用的8位來當(dāng)數(shù)據(jù)的大小微姊,IEEE浮點標(biāo)準(zhǔn)中就規(guī)定了這32位應(yīng)該如何使用酸茴。

它將位序列分為三個部分來理解,
第一部分:符號兢交,決定這個數(shù)是正數(shù)還是負(fù)數(shù)薪捍,一般用1表示負(fù)數(shù),0表示正數(shù)配喳。
第二部分:尾數(shù)酪穿,是一個二進(jìn)制小數(shù)。
第三部分:階碼晴裹,是對浮點數(shù)的加權(quán)被济。

可以這樣去理解,有點像科學(xué)計數(shù)法息拜,比如:
100溉潭,我們可以記為0.1 \times 10^3
0.257少欺,我們可以記為0.257 \times 10^0
257喳瓣,可以記為0.257 \times 10^3

IEEE標(biāo)準(zhǔn)中,給定了32位與64位浮點數(shù)赞别,各個部分的格式畏陕。
32位浮點數(shù):
最高位:符號位(1位)-階碼(8位)-尾數(shù)(23位):最低位
64位浮點數(shù):
最高位:符號位(1位)-階碼(11位)-尾數(shù)(52位):最低位

我們先假設(shè)一個8位的浮點數(shù),并通過將它的數(shù)值列在一個表格中來觀察學(xué)習(xí)浮點數(shù)的標(biāo)準(zhǔn)是如何工作的仿滔。
8位浮點數(shù)惠毁,我們設(shè)定最高的1位是符號位犹芹,0表示正數(shù),1表示負(fù)數(shù)鞠绰,接下來的4位表示階碼腰埂,最低3位表示尾數(shù)。請看下表蜈膨。

8-bit浮點數(shù)

上表屿笼,只列出了符號位是0的情況。

規(guī)格化數(shù)與非規(guī)格化數(shù)

我們注意A列的描述翁巍,浮點數(shù)大體上分為三種情況驴一,非規(guī)格化數(shù)規(guī)格化數(shù)灶壶,其他值

非規(guī)格化數(shù)的特征是肝断,階碼段的位都是0。
規(guī)格化數(shù)的特征是驰凛,階碼段不全為0胸懈,也不全為1。
其他值的特征就是洒嗤,階碼段全為1箫荡。

指數(shù)部分

然后,我們注意一下D列渔隶,有一個偏置值的概念羔挡,它的值是2^{k-1}-1k的值是階碼的長度(位寬)间唉,在我們自定義的8-bit浮點數(shù)中绞灼,k的值為4,所以偏置值是7呈野。

階碼E是分兩種情況的低矮。
當(dāng)這個浮點數(shù)是非規(guī)格化數(shù)的時候,E=1-偏置值
當(dāng)這個浮點數(shù)是規(guī)格化數(shù)的時候被冒,E=e-偏置值

e是階碼段的4-bit位序列所表示的無符號數(shù)军掂。

所以,表格中E列指的是4-bit位序列按無符號數(shù)解析的十進(jìn)制無符號數(shù)昨悼。

而F列蝗锥,則是按是否是規(guī)格化數(shù)來決定的值。這個偏置值的設(shè)定與補碼負(fù)權(quán)的設(shè)計是非常相似的率触。

最終指數(shù)部分就是2^E了终议。

小數(shù)部分

小數(shù)部分M是由最后三位決定的,它同樣是分情況的。

當(dāng)這個浮點數(shù)是非規(guī)格化數(shù)的時候穴张,位序列BBB應(yīng)當(dāng)理解為0.BBB细燎,也就是整數(shù)部分為0的二進(jìn)制小數(shù)。
當(dāng)這個浮點數(shù)是規(guī)格化數(shù)的時候皂甘,位序列BBB應(yīng)當(dāng)理解為1.BBB玻驻,也就是整數(shù)部分為1的二進(jìn)制小數(shù)。

此時偿枕,對照表格的H列與I列击狮,即可明白它們的含義。

浮點數(shù)的值

最終益老,這個浮點數(shù)的值就這樣得出來了value=sign \times 2^E \times M。sign是第一位決定符號的寸莫。

抽象數(shù)據(jù)模型(Abstract Data Models)

最近無意間看到一個這樣的概念捺萌,與計算機中的數(shù)有關(guān),就在這里提及下膘茎。

應(yīng)用與操作系統(tǒng)都有一個抽象數(shù)據(jù)模型桃纯,大部分應(yīng)用都沒有顯式的表現(xiàn)出這個模型,但是它會影響到代碼的編寫披坏,在32 bits programming model(ILP32)上态坦,integer, long, pointer都是32 bits的,大部分開發(fā)者都沒有意識到這一點棒拂。

現(xiàn)在系統(tǒng)擴展到64 bits伞梯,如果把所有的數(shù)據(jù)類型都擴展到64位是非常浪費的,因為很多應(yīng)用并不需要真的用到64位那么大的數(shù)據(jù)格式帚屉,但是pointer卻需要擴展到64位谜诫,所以在LLP64/P64上,pointer被擴展到64位攻旦,其他的仍然保持32位喻旷。

以上內(nèi)容翻譯自Abstract Data Models

抽象數(shù)據(jù)模型指定了編程語言中幾個基礎(chǔ)數(shù)據(jù)類型的大小。

比如LP64(可能是64-bit Leopard的縮寫)是使用在64位OSX系統(tǒng)或者Linux系統(tǒng)上的牢屋,它指定了integer為32位且预,long是64位,pointer是64位烙无。

還有LLP64锋谐,這是windows 64位操作系統(tǒng)所選擇的ADM,它的integer/long/pointer分別使用的是32/32/64位皱炉。

更細(xì)致的說明與討論怀估,我已經(jīng)整理好了參考資料給大家。

參考資料

有看不懂的地方請給我說,我再添加更詳細(xì)的解釋从藤;有講得不正確的地方還歡迎大家指正與討論:D

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末催跪,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子夷野,更是在濱河造成了極大的恐慌懊蒸,老刑警劉巖,帶你破解...
    沈念sama閱讀 210,914評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件悯搔,死亡現(xiàn)場離奇詭異骑丸,居然都是意外死亡,警方通過查閱死者的電腦和手機妒貌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評論 2 383
  • 文/潘曉璐 我一進(jìn)店門通危,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人灌曙,你說我怎么就攤上這事菊碟。” “怎么了在刺?”我有些...
    開封第一講書人閱讀 156,531評論 0 345
  • 文/不壞的土叔 我叫張陵逆害,是天一觀的道長。 經(jīng)常有香客問我增炭,道長忍燥,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,309評論 1 282
  • 正文 為了忘掉前任隙姿,我火速辦了婚禮梅垄,結(jié)果婚禮上坝咐,老公的妹妹穿的比我還像新娘析命。我一直安慰自己,他們只是感情好腹缩,可當(dāng)我...
    茶點故事閱讀 65,381評論 5 384
  • 文/花漫 我一把揭開白布欲鹏。 她就那樣靜靜地躺著机久,像睡著了一般。 火紅的嫁衣襯著肌膚如雪赔嚎。 梳的紋絲不亂的頭發(fā)上膘盖,一...
    開封第一講書人閱讀 49,730評論 1 289
  • 那天胧弛,我揣著相機與錄音,去河邊找鬼侠畔。 笑死结缚,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的软棺。 我是一名探鬼主播红竭,決...
    沈念sama閱讀 38,882評論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼喘落!你這毒婦竟也來了茵宪?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,643評論 0 266
  • 序言:老撾萬榮一對情侶失蹤瘦棋,失蹤者是張志新(化名)和其女友劉穎稀火,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體赌朋,經(jīng)...
    沈念sama閱讀 44,095評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡憾股,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,448評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了箕慧。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,566評論 1 339
  • 序言:一個原本活蹦亂跳的男人離奇死亡茴恰,死狀恐怖颠焦,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情往枣,我是刑警寧澤伐庭,帶...
    沈念sama閱讀 34,253評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站分冈,受9級特大地震影響圾另,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜雕沉,卻給世界環(huán)境...
    茶點故事閱讀 39,829評論 3 312
  • 文/蒙蒙 一集乔、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧坡椒,春花似錦扰路、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至丈攒,卻和暖如春哩罪,著一層夾襖步出監(jiān)牢的瞬間授霸,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評論 1 264
  • 我被黑心中介騙來泰國打工际插, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留碘耳,地道東北人。 一個月前我還...
    沈念sama閱讀 46,248評論 2 360
  • 正文 我出身青樓腹鹉,卻偏偏與公主長得像藏畅,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子功咒,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,440評論 2 348

推薦閱讀更多精彩內(nèi)容