W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗值獎勵
Note
在本書中,標(biāo)題帶有的 * 符號的是選讀章節(jié)。如果你時間有限或感到理解困難,可以先跳過,等學(xué)完必讀章節(jié)后再單獨攻克。
在上一節(jié)的表格中我們發(fā)現(xiàn),所有整數(shù)類型能夠表示的負(fù)數(shù)都比正數(shù)多一個,例如 byte 的取值范圍是 [?128,127] 。這個現(xiàn)象比較反直覺,它的內(nèi)在原因涉及到原碼、反碼、補碼的相關(guān)知識。
首先需要指出,數(shù)字是以“補碼”的形式存儲在計算機中的。在分析這樣做的原因之前,我們首先給出三者的定義。
圖 3-4 展示了原碼、反碼和補碼之間的轉(zhuǎn)換方法。
圖 3-4 原碼、反碼與補碼之間的相互轉(zhuǎn)換
「原碼 true form」雖然最直觀,但存在一些局限性。一方面,負(fù)數(shù)的原碼不能直接用于運算。例如在原碼下計算 1+(?2) ,得到的結(jié)果是 ?3 ,這顯然是不對的。
為了解決此問題,計算機引入了「反碼 1's complement code」。如果我們先將原碼轉(zhuǎn)換為反碼,并在反碼下計算 1+(?2) ,最后將結(jié)果從反碼轉(zhuǎn)化回原碼,則可得到正確結(jié)果 ?1 。
另一方面,數(shù)字零的原碼有 +0 和 ?0 兩種表示方式。這意味著數(shù)字零對應(yīng)著兩個不同的二進制編碼,其可能會帶來歧義。比如在條件判斷中,如果沒有區(qū)分正零和負(fù)零,則可能會導(dǎo)致判斷結(jié)果出錯。而如果我們想要處理正零和負(fù)零歧義,則需要引入額外的判斷操作,其可能會降低計算機的運算效率。
與原碼一樣,反碼也存在正負(fù)零歧義問題,因此計算機進一步引入了「補碼 2's complement code」。我們先來觀察一下負(fù)零的原碼、反碼、補碼的轉(zhuǎn)換過程:
在負(fù)零的反碼基礎(chǔ)上加 1 會產(chǎn)生進位,但 byte 類型的長度只有 8 位,因此溢出到第 9 位的 1 會被舍棄。也就是說,負(fù)零的補碼為 00000000 ,與正零的補碼相同。這意味著在補碼表示中只存在一個零,正負(fù)零歧義從而得到解決。
還剩余最后一個疑惑:byte 類型的取值范圍是 [?128,127] ,多出來的一個負(fù)數(shù) ?128 是如何得到的呢?我們注意到,區(qū)間 [?127,+127] 內(nèi)的所有整數(shù)都有對應(yīng)的原碼、反碼和補碼,并且原碼和補碼之間是可以互相轉(zhuǎn)換的。
然而,補碼 10000000 是一個例外,它并沒有對應(yīng)的原碼。根據(jù)轉(zhuǎn)換方法,我們得到該補碼的原碼為 00000000 。這顯然是矛盾的,因為該原碼表示數(shù)字 0 ,它的補碼應(yīng)該是自身。計算機規(guī)定這個特殊的補碼 10000000 代表 ?128 。實際上,(?1)+(?127) 在補碼下的計算結(jié)果就是 ?128 。
你可能已經(jīng)發(fā)現(xiàn),上述的所有計算都是加法運算。這暗示著一個重要事實:計算機內(nèi)部的硬件電路主要是基于加法運算設(shè)計的。這是因為加法運算相對于其他運算(比如乘法、除法和減法)來說,硬件實現(xiàn)起來更簡單,更容易進行并行化處理,運算速度更快。
請注意,這并不意味著計算機只能做加法。通過將加法與一些基本邏輯運算結(jié)合,計算機能夠?qū)崿F(xiàn)各種其他的數(shù)學(xué)運算。例如,計算減法 a?b 可以轉(zhuǎn)換為計算加法 a+(?b) ;計算乘法和除法可以轉(zhuǎn)換為計算多次加法或減法。
現(xiàn)在我們可以總結(jié)出計算機使用補碼的原因:基于補碼表示,計算機可以用同樣的電路和操作來處理正數(shù)和負(fù)數(shù)的加法,不需要設(shè)計特殊的硬件電路來處理減法,并且無須特別處理正負(fù)零的歧義問題。這大大簡化了硬件設(shè)計,提高了運算效率。
補碼的設(shè)計非常精妙,因篇幅關(guān)系我們就先介紹到這里,建議有興趣的讀者進一步深度了解。
細(xì)心的你可能會發(fā)現(xiàn):int 和 float 長度相同,都是 4 bytes,但為什么 float 的取值范圍遠(yuǎn)大于 int ?這非常反直覺,因為按理說 float 需要表示小數(shù),取值范圍應(yīng)該變小才對。
實際上,這是因為浮點數(shù) float 采用了不同的表示方式。記一個 32-bit 長度的二進制數(shù)為:
根據(jù) IEEE 754 標(biāo)準(zhǔn),32-bit 長度的 float 由以下三個部分構(gòu)成。
二進制數(shù) float 對應(yīng)的值的計算方法:
轉(zhuǎn)化到十進制下的計算公式:
其中各項的取值范圍:
圖 3-5 IEEE 754 標(biāo)準(zhǔn)下的 float 的計算示例
觀察圖 3-5 ,給定一個示例數(shù)據(jù) S=0 , E=124 ,N= 2^(?2)+2^(?3)=0.375 ,則有:
現(xiàn)在我們可以回答最初的問題:float 的表示方式包含指數(shù)位,導(dǎo)致其取值范圍遠(yuǎn)大于 int 。根據(jù)以上計算,float 可表示的最大正數(shù)為 2^(254?127)×(2?2^(?23))≈3.4×10^38 ,切換符號位便可得到最小負(fù)數(shù)。
盡管浮點數(shù) float 擴展了取值范圍,但其副作用是犧牲了精度。整數(shù)類型 int 將全部 32 位用于表示數(shù)字,數(shù)字是均勻分布的;而由于指數(shù)位的存在,浮點數(shù) float 的數(shù)值越大,相鄰兩個數(shù)字之間的差值就會趨向越大。
如表 3-2 所示,指數(shù)位 E=0 和 E=255 具有特殊含義,用于表示零、無窮大、NaN 等。
表 3-2 指數(shù)位含義
指數(shù)位 E | 分?jǐn)?shù)位 | 分?jǐn)?shù)位 | 計算公式 |
---|---|---|---|
次正規(guī)數(shù) | |||
正規(guī)數(shù) | 正規(guī)數(shù) | ||
值得說明的是,次正規(guī)數(shù)顯著提升了浮點數(shù)的精度。最小正正規(guī)數(shù)為 2^(?126) ,最小正次正規(guī)數(shù)為 2^(?126)×2^(?23) 。
雙精度 double 也采用類似 float 的表示方法,在此不做贅述。
Copyright©2021 w3cschool編程獅|閩ICP備15016281號-3|閩公網(wǎng)安備35020302033924號
違法和不良信息舉報電話:173-0602-2364|舉報郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯(lián)系方式:
更多建議: