第二章 信息的表示和處理

[清晰svg格式從此提取](http://pan.baidu.com/s/1hscLNa0)

本章我們來研究三種重要的數(shù)字表示

  • 無符號是基于傳統(tǒng)二進(jìn)制表示法,表示大于或等于0的數(shù)字
  • 補(bǔ)碼是表示有符號整數(shù)的最常見方式,有符號整數(shù)可以是正或者為負(fù)
  • 浮點(diǎn)數(shù)是表示實(shí)數(shù)的科學(xué)計數(shù)法的以2為基數(shù)的版本

計算機(jī)的表示法是用有限的數(shù)量的位表示的數(shù)字編碼,所以,結(jié)果太大的時候,某些運(yùn)算就會溢出。浮點(diǎn)運(yùn)算溢出會產(chǎn)生特殊的值+∞,但是一組正數(shù)的乘積總是正的,這點(diǎn)和整數(shù)不同。但是由于表示的精度有限,浮點(diǎn)運(yùn)算是不可結(jié)合的。

整數(shù)的表示雖然只能編碼一個相對較小的數(shù)值范圍,但是這種表示是精確的,而浮點(diǎn)數(shù)可以編碼一個較大的數(shù)值范圍,但是這種表示只是近似的。

2.1信息存儲

大多數(shù)計算機(jī)使用8位的塊,或者說字節(jié),作為最小的可尋址的內(nèi)存單位,而不是訪問內(nèi)存中單獨(dú)的位。機(jī)器級程序?qū)?nèi)存視為一個非常大的字節(jié)數(shù)組,稱為虛擬內(nèi)存。內(nèi)存的每個字節(jié)都由一個唯一的數(shù)字標(biāo)識,稱為它的地址,所有可能地址的集合稱為虛擬地址空間。

十六進(jìn)制表示法

一個字節(jié)由8位組成,在二進(jìn)制表示法中,它的值域是0000000011111111,用十六進(jìn)制書寫,它的值域是0x000xFF。

以0x或者0X開頭的數(shù)字常量被認(rèn)為是16進(jìn)制,字符“A”~“F”既可以是大寫也可以是小寫。

字?jǐn)?shù)據(jù)大小

字長決定的最重要的系統(tǒng)參數(shù)就是虛擬地址空間的最大大小。對于一個字長為ω位的機(jī)器而言,虛擬地址的范圍就是0 ~ 2^ω - 1,程序最多訪問2^ω個字節(jié)。

大部分?jǐn)?shù)據(jù)類型都編碼為有符號數(shù)值,除非有前綴關(guān)鍵字unsigned或者對確定大小的數(shù)據(jù)類型使用了特定的無符號聲明。(數(shù)據(jù)類型char是個例外)

尋址和字節(jié)順序

多字節(jié)對象被存儲為連續(xù)的字節(jié)序列,對象的地址為所用字節(jié)最小的地址。

最低有效字節(jié)在最前面的方式,稱為小端法
最高有效字節(jié)在最前面的方式,稱為大端法

許多比較新的微處理器是雙端法,然而實(shí)際上,一旦選擇了特定的操作系統(tǒng),字節(jié)順序也就被固定下來。Android和iOS都是運(yùn)行于小端模式。

表示代碼

指令編碼是不同的,不同的機(jī)器類型使用不同的且不兼容的指令和編碼方式。
從機(jī)器的角度來看,程序僅僅只是字節(jié)序列。

有關(guān)布爾代數(shù)

布爾運(yùn)算~對應(yīng)邏輯運(yùn)算NOT,對應(yīng)集合的補(bǔ);
布爾運(yùn)算&對應(yīng)邏輯運(yùn)算AND,對應(yīng)集合的并;
布爾運(yùn)算|對應(yīng)邏輯運(yùn)算OR,對應(yīng)集合的交;
布爾運(yùn)算^對應(yīng)邏輯運(yùn)算亦或。

布爾運(yùn)算的分配率:a & (b | c) = (a & b)|(a & c),a | (b & c) = (a | b)&(a | c)
另外,對于a ^ a = 0,因此還有(a ^ b) ^ a = b

這里有一些有意思的關(guān)于位運(yùn)算的東西,簡單整理在一起,以后單開一個筆記,把一些位運(yùn)算的算法題歸類一下。

a ^= b;
b ^= a;
a ^= b;

這樣可以交換a和b的值,但是這個種交換方式?jīng)]有性能上的優(yōu)勢,它僅僅是一個智力游戲(至少書上是這么說的……)

有關(guān)邏輯運(yùn)算

C語言還提供了一組邏輯運(yùn)算符||、&&、和!,分別對應(yīng)命題邏輯中的OR,AND和NOT運(yùn)算。

邏輯運(yùn)算具有短路性。即:第一個參數(shù)求值能確定表達(dá)結(jié)果,那么就不會對第二個參數(shù)求值。

有關(guān)位移運(yùn)算

C語言提供了位移運(yùn)算,向左或者向右移動位模式。

C表達(dá)式x << k會x向左移動k位,丟棄最高的k位,并在右端補(bǔ)k個0.

有一個相應(yīng)的友誼運(yùn)算x >> k。一般而言,機(jī)器支持兩種形式的右移:邏輯右移和算術(shù)右移。邏輯右移在左端補(bǔ)k個0,算數(shù)右移是在左端補(bǔ)k個最高有效位的值。

對于無符號數(shù),右移必須是邏輯的。

Java表達(dá)式中,x >> k是算術(shù)右移,x >>> k是邏輯右移。

加減法優(yōu)先級比位移優(yōu)先級要高

    一些位運(yùn)算的常見技巧
    
    0 ^ a = a
    
    a ^ a = 0
    
    a & (a - 1)可以消除最右邊的一個1
    
    位運(yùn)算x & 0xFF生成一個由x的最低有效字節(jié)組成的值,而其他字節(jié)就被置為0。
    
    Java是用的補(bǔ)碼,0x7fffffff是最大正整數(shù),0x80000000是最小的負(fù)數(shù)。
    和0x7fffffff按位與就是取絕對值了,然后那個按位或就是求負(fù)數(shù)。
    
    可以用if ((a & 1) == 0)代替if (a % 2 == 0)來判斷a的奇偶性。
    
    修改正負(fù)號就是按位取反再加一,也就是~x + 1
    
    檢驗(yàn)補(bǔ)碼乘法是否溢出:
    int p = x * y;
    return !x || p / x == y;
    
    

2.2整數(shù)表示

整型數(shù)據(jù)類型表示有限范圍的整數(shù)。

有一個很值得注意的特點(diǎn)是取值范圍是不對稱的,負(fù)數(shù)的范圍比正數(shù)的范圍大1.

無符號數(shù)的編碼

無符號數(shù)編碼詳見思維導(dǎo)圖。

補(bǔ)碼編碼

補(bǔ)碼編碼詳見思維導(dǎo)圖。

ω位補(bǔ)碼所能表示的值的范圍:最小是位向量[10……0],最大的值是位向量[01……1]。

注意:-1和UMax有同樣的位表示——全1的串。數(shù)值0在兩種表示方式中都是全0的串。

有符號數(shù)和無符號數(shù)之間的轉(zhuǎn)換

C語言允許在各種不同的數(shù)字?jǐn)?shù)據(jù)類型之間做強(qiáng)制轉(zhuǎn)換。

強(qiáng)制類型轉(zhuǎn)換的結(jié)果保持位值不變,只是改變了解釋這些位的方式。

補(bǔ)碼轉(zhuǎn)無符號數(shù)詳見思維導(dǎo)圖。

無符號數(shù)轉(zhuǎn)為補(bǔ)碼詳見思維導(dǎo)圖。

執(zhí)行一個運(yùn)算的時候,如果它的一個運(yùn)算數(shù)是有符號的而另一個是無符號的,那么C語言會隱式的將有符號數(shù)強(qiáng)制類型轉(zhuǎn)換為無符號數(shù),并假設(shè)兩個數(shù)都是非負(fù)的,來執(zhí)行運(yùn)算。

擴(kuò)展一個數(shù)字的位表示

要將一個無符號數(shù)轉(zhuǎn)換為一個更大的數(shù)據(jù)類型,只需要簡單的在表示的開頭添加0,這種運(yùn)算被稱為零擴(kuò)展

要將一個補(bǔ)碼數(shù)字轉(zhuǎn)換為一個更大的數(shù)據(jù)類型,可以執(zhí)行一個符號擴(kuò)展,在表示中添加最高有效位的值。

一個有意思的地方:
當(dāng)把short轉(zhuǎn)換成unsigned時,我們要先改變大小,之后再完成從有符號到無符號的轉(zhuǎn)換。也就是說(unsigned)sx等價于(unsigned)(int)sx

截斷數(shù)字

將一個ω位的數(shù)字截斷為一個k位的數(shù)字,我們會丟棄最高的ω - k位。

補(bǔ)碼截斷也有相似的屬性,不過要將最高位轉(zhuǎn)換為符號位,也就是說,一個正數(shù)截斷了以后可能就變成了負(fù)數(shù)。

2.3整數(shù)運(yùn)算

無符號加法

詳見思維導(dǎo)圖

如果s沒有溢出,則可以肯定s ≥ x

無符號數(shù)求反詳見思維導(dǎo)圖

補(bǔ)碼加法

詳見思維導(dǎo)圖

對滿足TMinω ≤ x, y ≤ TMaxω 的x和y,另s = x + y,當(dāng)且僅當(dāng)x > 0,y > 0,但s ≤ 0時,計算s發(fā)生了正溢出。當(dāng)且僅當(dāng)x < 0,y < 0,但s ≥ 0時,計算s發(fā)生了負(fù)溢出。

補(bǔ)碼的非

詳見思維導(dǎo)圖

無符號乘法

詳見思維導(dǎo)圖

補(bǔ)碼乘法

詳見思維導(dǎo)圖

乘以常數(shù)

編譯器使用了一項(xiàng)重要優(yōu)化,試著用位移和加法運(yùn)算的組合來代替乘以常數(shù)因子的乘法。

考慮一組從位位置n到位位置m的連續(xù)的1(n≥m)我們可以用下面兩種不同形式中的一種來計算這些位對乘積的影響:

  • 形式A:(x << n) + (x << n - 1) + ... + (x << m)
  • 形式B:(x << (n + 1)) - (x << m)

除以2的冪

  • 除以2的冪無符號除法:C變量x和k有無符號數(shù)值x和k,且0 ≤ k < ω,則x>>k產(chǎn)生數(shù)值,x / 2^k
  • 除以2的冪補(bǔ)碼除法,向下舍入:C變量補(bǔ)碼x和無符號k,且0 ≤ k < ω,當(dāng)執(zhí)行算術(shù)位移時,x>>k產(chǎn)生數(shù)值x / 2^k
  • 除以2的冪的補(bǔ)碼除法,向上舍入:C變量補(bǔ)碼x和無符號k,且0 ≤ k < ω,當(dāng)執(zhí)行算術(shù)位移時,(x + (1 << k)- 1) >> k產(chǎn)生數(shù)值x / 2^k。

遺憾的是,和乘法不同,我們不能用除以2的冪的出發(fā)來表示任意常數(shù)K的除法,所以除法絕大多數(shù)情況下指令會相當(dāng)慢。

2.4浮點(diǎn)數(shù)

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

定義見思維導(dǎo)圖

IEEE浮點(diǎn)表示

IEEE浮點(diǎn)標(biāo)準(zhǔn)用V = (-1)^s * M * 2^E

  • s:sign,符號,決定正負(fù)。
  • M:significand,尾數(shù),通常是[1.0~2.0)范圍的小數(shù)
  • E:exponent,階碼,就是次方數(shù)。

單精度浮點(diǎn)格式中,s,exp和frac字段分別為1位、k = 8位和n = 23位,得到一個32位的表示。雙精度浮點(diǎn)格式中,s、exp和frac字段分別為1位、k = 11位和n = 52位,得到一個64位的表示。

規(guī)格化的值

解碼字段被解釋為偏置形式表示的有符號整數(shù)。階碼的值是E = e - Bias,其中e是無符號數(shù),而Bias是一個等于2^(k-1) - 1的偏置值。

小數(shù)子段frac被解釋為描述小數(shù)值f,其中0 ≤ f < 1,二進(jìn)制小數(shù)在最高有效為的左邊。尾數(shù)定義為M = 1 + f。這種方式也叫做隱含的以1開頭的表示.

非規(guī)格化的值

階碼值是E = 1 - Bias,而尾數(shù)的值是M = f,也就是小數(shù)字段的值,不包含隱含的開頭的1.

非規(guī)格化數(shù)的另外一個功能是表示那些非常接近于0.0的數(shù)。它們提供了一種屬性,稱為逐漸溢出,其中,可能的數(shù)值分布均勻的接近于0.0.

特殊值

最后一類數(shù)值是當(dāng)階碼全為1的時候出現(xiàn)的。當(dāng)小數(shù)域全為0時,得到的值表示無窮,當(dāng)s = 0時是+∞,當(dāng)s = 1時是-∞。當(dāng)我們把兩個非常大的數(shù)相乘,或者除以零時,無窮能夠表示溢出的結(jié)果。當(dāng)小數(shù)域?yàn)榉橇愕臅r候,結(jié)果值被稱為“NaN”,即“不是一個數(shù)(Not a Number)”的縮寫。一些運(yùn)算的結(jié)果不能是實(shí)數(shù)或無窮,就會返回這樣的NaN值,比如當(dāng)計算根號-1或者∞-∞時。

浮點(diǎn)數(shù)的一些Point

  • 值+0.0總有一個全為0的位表示。
  • 最小的正非規(guī)格化的位表示,是有最低有效位為1而其他所有位為0構(gòu)成的。它的數(shù)字值是V = 2(-n-2(k-1)+2)。
  • 最大的非規(guī)格化值的位模式是由全為0的解碼字段和全為1的小叔子段組成的。數(shù)值V = (1 - 2^(-n)) * 2((-2)(k-1)+2)。
  • 最小的正規(guī)格化值的位模式的階碼字段的最低有效位為1,其他位全為0。它的尾數(shù)值M = 1,而階碼值E = -2^(k-1) + 2。因此數(shù)值V = 2((-2)(k-1) + 2)。
  • 值1.0的位表示的階碼字段除了最高有效為等于1以外,其他位都等于0。它的尾數(shù)值是M = 1,他的階碼值是E = 0.
  • 最大的規(guī)格化值的位表示的符號位為0,階碼的最低有效位等于0,其他位等于1.數(shù)值V = (1 - 2^(-n-1)) * 22(k-1)

舍入

IEEE浮點(diǎn)格式定義了四種不同的舍入方式。默認(rèn)的方法是找到最接近的匹配,其他三種可用于計算上界和下界。

浮點(diǎn)運(yùn)算

IEEE標(biāo)準(zhǔn)定義了一些使之更合理的規(guī)則。例如,定義1 / -0將產(chǎn)生-∞,而定義1 / +0會產(chǎn)生+∞

對于所有的x和y的值,實(shí)數(shù)的加法運(yùn)算是可交換的。但是運(yùn)算是不可結(jié)合的。例如(3.14 + 1e10)- 1e10

無窮(因?yàn)?∞ - ∞ = NaN)和 NaN是例外情況,因?yàn)閷τ谌魏蝬都有NaN + x = NaN。

浮點(diǎn)加法滿足了單調(diào)性屬性:如果a ≥ b,那么對任何a、b以及x的值,除了NaN,都有x + a ≥ x + b。

浮點(diǎn)乘法也遵循通常乘法所具有的許多屬性。它是可交換的,不具有可結(jié)合行。例如(1e20 * 1e20) * 1e-20位+∞,而1e20 * (1e20 * 1e-20)得出1e20。

浮點(diǎn)乘法滿足單調(diào)性:

a ≥ b 且 c ≥ 0 → a * c ≥ b * c
a ≥ b 且 c ≤ 0 → a * c ≤ b * c

我們還可以保證,只要a ≠ NaN,就有a * a ≥ 0.

C語言中的浮點(diǎn)數(shù)

所有C語言版本提供了兩種不同的浮點(diǎn)數(shù)據(jù)類型:float 和 double。

一些會產(chǎn)生錯誤的點(diǎn):

  • 從int轉(zhuǎn)成float,數(shù)字不會溢出,但是可能被舍入。
  • 從int或float轉(zhuǎn)成double,因?yàn)閐ouble有更大的范圍,也有更高的精度,所以能夠保留精確的數(shù)值。
  • 從double轉(zhuǎn)換成float,因?yàn)榉秶。灾悼赡芤绯龀?∞或者-∞,另外,也可能舍入。
  • 從float或double轉(zhuǎn)成int,值將會向零舍入。例如1.999轉(zhuǎn)換為1,而-1.999轉(zhuǎn)為-1.進(jìn)一步來說,值可能會溢出。與Inter兼容的微處理器指定位模式[10...00](字長為ω時的TMinω)為整數(shù)不確定值。一個從浮點(diǎn)數(shù)到整數(shù)的轉(zhuǎn)換,如果不能為該浮點(diǎn)數(shù)找到一個合理的整數(shù)近似值,就會產(chǎn)生一個這樣的值。因此,表達(dá)式(int)+le10會得到-21483648,即從一個正值變成了一個負(fù)值!!!

課后題中的一些點(diǎn)

  1. 表達(dá)式~0xFF創(chuàng)建一個掩碼,該掩碼8個最低位為0,其余的為1.這個掩碼的產(chǎn)生與字長無關(guān)。相比之下,0xFFFFFF00只能工作在32位機(jī)器上。
  2. x ^ y = (x & ~y) | (~x & y)
  3. TMin32是-2147483648,并且將它強(qiáng)制類型轉(zhuǎn)換為無符號數(shù)后,變成了2147483648。另外,如果有任何一個運(yùn)算數(shù)是無符號的,那么在比較之前,另一個運(yùn)算數(shù)會被強(qiáng)制類型轉(zhuǎn)換為無符號數(shù)。
  4. 函數(shù)的任何測試過程中,TMin 都應(yīng)該作為一種測試情況!!!
  5. 對于無符號數(shù)的非,位的模式是相同的。
  6. int64_t pll = (int64_t) x * y;這一句如果寫成int64_t pll = x * y;就會用32位的值來計算乘積(這時候可能就溢出了!),然而再符號擴(kuò)展到64位。
  7. 表達(dá)式x>>32產(chǎn)生一個字,如果x是負(fù)數(shù),這個字全為1,否則全為0.這個訣竅偶爾會有大用處。
  8. 一個具有n位小數(shù)的浮點(diǎn)格式,不能準(zhǔn)確描述的最小正整數(shù)是:1后面跟著n個0后面再跟一個1.即2^(n+1) + 1
  9. 2.44C的謎題里的需要理解的計算機(jī)運(yùn)算的屬性。
32位機(jī)器,有符號值使用算術(shù)右移,無符號值使用邏輯右移
int x,y
unsigned ux,uy

A. (x > 0)||((x - 1) < 0
False 假設(shè)x = TMin32,那么x - 1 = TMax32
B.  (x & 7) != 7 || (x << 29 < 0)
True
C.  (x * x) >= 0
False 當(dāng)x為0xFFFF時,x * x為-131071(0xFFFE0001)
D.  x < 0 || -x <= 0
True
E.  x > 0 || -x >= 0
False 設(shè)x = TMin32(好吧,又是它)那么x和-x都是負(fù)數(shù)!
F.  x + y == uy + ux
True
G.  x * ~y + uy * ux == -x
True ~y等于-y-1.uy * ux等于x * y,所以左邊等價于x * -y - x + x * y。

勘誤:43頁第一段原文中寫的是復(fù)數(shù)的范圍比整數(shù)的范圍大1,應(yīng)該是正數(shù)……

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,908評論 6 541
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,324評論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,018評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,675評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,417評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,783評論 1 329
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,779評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,960評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,522評論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,267評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,471評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,009評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,698評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,099評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,386評論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,204評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,436評論 2 378

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