0.目錄
- 浮點(diǎn)數(shù)的表示
- 加減
- 存儲(chǔ)格式
- 特殊的數(shù)
1.浮點(diǎn)數(shù)的表示
1.1 表示格式
浮點(diǎn)數(shù)月匣,顧名思義,是小數(shù)點(diǎn)不固定的數(shù)萍悴。計(jì)算機(jī)中寓免,根據(jù)小數(shù)點(diǎn)位置是否固定狡刘,分為兩種數(shù)據(jù)格式,一種小數(shù)點(diǎn)不固定剑按,另一種是定點(diǎn)數(shù)猬腰,小數(shù)點(diǎn)固定姑荷。
書上科學(xué)地對(duì)浮點(diǎn)數(shù)表示法的定義是鼠冕,以適當(dāng)?shù)男问綄⒈壤蜃颖硎驹跀?shù)據(jù)中懈费,讓小數(shù)點(diǎn)的位置根據(jù)需要而浮動(dòng)憎乙。我們計(jì)算機(jī)的容量有限泞边,不可能對(duì)每個(gè)數(shù)都用特別多的位數(shù)來表示阵谚,如2×10^99椭蹄,這種非常大的數(shù)不可能用定點(diǎn)數(shù)來表示绳矩,所以利用浮點(diǎn)數(shù)可以在位數(shù)有限的情況下擴(kuò)大數(shù)的表示范圍翼馆,同時(shí)保持一定的有效精度严沥。
通常情況下消玄,浮點(diǎn)數(shù)表示為:N = r ^ E × M
- r 是浮點(diǎn)數(shù)階碼的底翩瓜,在計(jì)算機(jī)中是隱含的兔跌,通常情況下r=2
- E叫做階碼华望,是帶符號(hào)的定點(diǎn)數(shù)赖舟,E的大小越大建蹄,能表示的數(shù)范圍越大
- M叫做尾數(shù),是帶符號(hào)的定點(diǎn)數(shù)劲腿,M的位數(shù)越大焦人,數(shù)的有效精度越高
如1.111×2100花椭,1.111是尾數(shù),100是階碼郭厌,階碼位數(shù)為3位折柠,尾數(shù)位數(shù)是4位前塔。若階碼位數(shù)有4位承冰,尾數(shù)位數(shù)是3位(總占位數(shù)不變)巷懈,那這個(gè)數(shù)表示為1.11×20100凑保,能表示的數(shù)的范圍變大,原來尾數(shù)1.111轉(zhuǎn)變?yōu)?.11損失了0.001芝此,這就是精度的損失婚苹。
浮點(diǎn)數(shù)的一般格式:
這里J是階符谭企,表示階碼的符號(hào)廓译,S是數(shù)符,表示浮點(diǎn)數(shù)的符號(hào)债查,階符J和階碼的m位合起來表示浮點(diǎn)數(shù)的表示范圍和小數(shù)點(diǎn)的實(shí)際位置非区,n位尾數(shù)反映了浮點(diǎn)數(shù)的精度。
1.2 規(guī)格化
同一個(gè)浮點(diǎn)數(shù)可以有很多表現(xiàn)形式盹廷,如1.111×23征绸,可以表示為0.1111×24或11.11×22。如果尾數(shù)位數(shù)只有4位速和,表示同一個(gè)數(shù)1111可以采取兩種方法歹垫, 0.0111x25 和 0.1111×24(二者最高為是數(shù)符位S)暮芭,可以很清晰地看到砾莱,如果采用階碼為5的方法,我們損失了一位精度,階碼為4的方法表示這個(gè)數(shù)更為精確。
所以,為了提高運(yùn)算精度褪尝,就要充分利用尾數(shù)的有效數(shù)位沙庐,也就是浮點(diǎn)數(shù)的規(guī)格化铸抑,即規(guī)定尾數(shù)的最高數(shù)位必須是一個(gè)有效值刁憋。非規(guī)格化數(shù)轉(zhuǎn)變?yōu)橐?guī)格化數(shù)是尖,轉(zhuǎn)變過程就是通過調(diào)整尾數(shù)和階碼的大小,使尾數(shù)最高位保證是一個(gè)有效值凶硅。通常有兩種規(guī)格化操作:
- 左規(guī):當(dāng)浮點(diǎn)數(shù)運(yùn)算結(jié)果是非規(guī)格化數(shù)的時(shí)候粹污,要進(jìn)行規(guī)格化操作,將尾數(shù)算術(shù)左移一位,階碼減1(基數(shù)為2時(shí))杨凑,左規(guī)操作可能要進(jìn)行多次昭躺。
- 右規(guī):當(dāng)浮點(diǎn)數(shù)運(yùn)算結(jié)果尾數(shù)出現(xiàn)溢出的時(shí)候,將尾數(shù)算術(shù)右移一位,階碼加1。需要右規(guī)操作的時(shí)候只需要操作一次。
規(guī)格化浮點(diǎn)數(shù)的尾數(shù)M的絕對(duì)值應(yīng)該滿足這樣的關(guān)系:1/r ≤ |M| ≤ 1(r就是我們的階碼的底蝗蛙,也是基數(shù))壮韭。
以r=2為例:
- 原碼尾數(shù)規(guī)格化后:正數(shù)為0.1xxxxxx形式密任,最大值為0.11......1;最小值為0.100......0。負(fù)數(shù)為1.1xxxxxx形式,最大值表示為1.10......0;最小值表示為1.11......1淤年。
- 補(bǔ)碼尾數(shù)規(guī)格化后:正數(shù)同原碼正數(shù)塔插。負(fù)數(shù)為1.0xxxxxx形式逢倍,最大值表示為1.01......1;最小值表示為1.00......0。
需要注意的是基數(shù)趁怔,剛剛是以基數(shù)為2時(shí)的規(guī)格化形式,補(bǔ)碼規(guī)格化數(shù)的尾數(shù)最高位一定與尾數(shù)符號(hào)位相反秉沼。基數(shù)不同的時(shí)候辜腺,規(guī)格化的形式不同呜投,當(dāng)基數(shù)為4時(shí),原碼規(guī)格化形式的尾數(shù)最高兩位不全為0亩进;基數(shù)為8時(shí),原碼規(guī)格化形式的尾數(shù)最高三位不全為0缩歪。
如何判斷一個(gè)浮點(diǎn)數(shù)是否是規(guī)格化數(shù):規(guī)格化浮點(diǎn)數(shù)的尾數(shù)小數(shù)點(diǎn)后的第一位一定是個(gè)非零的數(shù)归薛。因此對(duì)于原碼編碼的尾數(shù)來說,只要看尾數(shù)的第一位是否為1就行匪蝙;對(duì)于補(bǔ)碼表示的尾數(shù)主籍,只要看符號(hào)位和尾數(shù)最高位是否相反。(IEEE 754標(biāo)準(zhǔn)的浮點(diǎn)數(shù)尾數(shù)是用原碼編碼的)
2.加減
浮點(diǎn)數(shù)運(yùn)算的特點(diǎn)是階碼運(yùn)算和尾數(shù)運(yùn)算分開來算逛球。加減運(yùn)算一律采用補(bǔ)碼千元。具體運(yùn)算分為以下幾步。
- 對(duì)階:目的是讓兩個(gè)數(shù)小數(shù)點(diǎn)的位置看齊颤绕,使兩個(gè)數(shù)的階碼相等幸海。顯然1.1×23 和1.1×24 是不能直接相加減的祟身。原則是小階向大階看齊,像這個(gè)例子物独,就是1.1×23 的尾數(shù)右移一位袜硫,階碼加一,直到兩個(gè)數(shù)的階碼相等
- 尾數(shù)求和:階碼對(duì)齊之后直接按照定點(diǎn)數(shù)的加減法則運(yùn)算尾數(shù)
- 規(guī)格化:尾數(shù)求和后的結(jié)果如果不是規(guī)格化數(shù)需要規(guī)格化议纯,以雙符號(hào)位運(yùn)算為例父款,如果運(yùn)算結(jié)果為正數(shù),規(guī)格化的形式應(yīng)該是00.1xxx......x瞻凤,如果運(yùn)算結(jié)果為負(fù)數(shù)憨攒,規(guī)格化后的形式應(yīng)該是11.0xxx......x,不符合這種形式的數(shù)要進(jìn)行左規(guī)或者右規(guī)的操作讓其變成這種形式阀参。(在尾數(shù)沒有溢出的情況下肝集,即尾數(shù)結(jié)果的雙符號(hào)為不是10或01的時(shí)候,操作都是左規(guī)操作蛛壳,左規(guī)操作可能不止進(jìn)行一次杏瞻,倘若雙符號(hào)位為01或10則表明尾數(shù)已經(jīng)溢出了,就要進(jìn)行右規(guī)操作衙荐,右規(guī)只需要進(jìn)行一次)
- 舍入:在對(duì)階和右規(guī)的操作中都是將尾數(shù)右移捞挥,階碼加一,由于位數(shù)是有限的忧吟,在右移的操作過程中很有可能就將低位的尾數(shù)丟失砌函,引起誤差和精度問題。常用的減小誤差的方法有“0”舍“1”入法:即在尾數(shù)右移時(shí)溜族,被移去的最高數(shù)值位為0則舍去讹俊,如果被移去的最高數(shù)值位為1則在尾數(shù)末位加1,如果加1之后又產(chǎn)生溢出則再右規(guī)操作一次煌抒。恒置“1”法:看名字就可以知道仍劈,無論丟掉的最高數(shù)值位是1還是0,都使右移后的尾數(shù)末位置1寡壮。這種方法可能使尾數(shù)變大或者變小贩疙。
- 溢出判斷:既然定點(diǎn)數(shù)運(yùn)算可能溢出,浮點(diǎn)數(shù)同樣也會(huì)溢出况既,我們已經(jīng)知道浮點(diǎn)數(shù)的表示方法和加減運(yùn)算規(guī)則屋群,既然是溢出,那么肯定是超出了浮點(diǎn)數(shù)能表示的范圍坏挠,浮點(diǎn)數(shù)的范圍主要是由階碼決定的芍躏,如果運(yùn)算結(jié)果規(guī)格化后階碼產(chǎn)生了溢出,那才是浮點(diǎn)數(shù)的溢出降狠。浮點(diǎn)數(shù)的溢出與否是由階碼的符號(hào)決定的对竣。以雙符號(hào)位的補(bǔ)碼為例庇楞,如果階碼的符號(hào)位出現(xiàn)01或10則說明階碼溢出了,01表示階碼大于最大階碼否纬,上溢吕晌,進(jìn)入中斷處理;10表示階碼小于最下階碼临燃,下溢睛驳,按機(jī)器零處理。(溢出時(shí)真值的符號(hào)位和高位符號(hào)位保持一致)還要注意的一點(diǎn)是尾數(shù)之和(差)可能會(huì)造成尾數(shù)的溢出膜廊,這并不代表整個(gè)的溢出乏沸,需要右規(guī)一次看階碼是否溢出才能判斷。
3.存儲(chǔ)格式
IEEE754標(biāo)準(zhǔn)規(guī)定爪瓜,浮點(diǎn)數(shù)由“符號(hào)”蹬跃、“指數(shù)”和“尾數(shù)”3部分構(gòu)成:
下表列出C++中不同精度浮點(diǎn)數(shù)內(nèi)存布局:
精度 | C++類型 | 長度 | 符號(hào)位數(shù) | 指數(shù)位數(shù) | 尾數(shù)位數(shù) | 有效位數(shù) | 指數(shù)偏移 | 隱含位 |
---|---|---|---|---|---|---|---|---|
單精度浮點(diǎn)數(shù) | float | 32 | 1 | 8 | 23 | 24 | 127 | 1個(gè)隱含位 |
雙精度浮點(diǎn)數(shù) | double | 64 | 1 | 11 | 52 | 53 | 1023 | 1個(gè)隱含位 |
擴(kuò)展雙精度浮點(diǎn)數(shù) | long double | 80 | 1 | 15 | 64 | 64 | 16383 | 無隱含位 |
float的規(guī)格化表示為:±1.f×2E?127,f是尾數(shù)铆铆,E是指數(shù)蝶缀。以float為例:
比如十進(jìn)制數(shù)123.125,其二進(jìn)制表示為:1111011.001薄货,規(guī)格化表示為:1.111011001×26翁都,也就是1.111011001×2133?127,f = 111011001谅猾,E = 133柄慰,二進(jìn)制為10000101,圖示如下:
規(guī)格化表示的浮點(diǎn)數(shù)赊瞬,整數(shù)位固定為1,可以省略贼涩,所以用23位可以存儲(chǔ)24位的尾數(shù)巧涧。這里可以得出一個(gè)結(jié)論:任意一個(gè)int值(二進(jìn)制表示),只要存在這樣的序列:從最低位開始找到第一個(gè)1遥倦,然后從這個(gè)1向高位數(shù)移動(dòng)24位停下谤绳,如果更高的位上不再有1,那么該int值即可被float精確表示袒哥,否則就不行缩筛。簡(jiǎn)單說,就是第一個(gè)1開始到最后一個(gè)1為止的總位數(shù)超過24堡称,那么該int值就不能被float類型精確表示瞎抛,例:
圖中能被丟棄的0,在指數(shù)上體現(xiàn)出來却紧,丟棄一個(gè)0桐臊,指數(shù)就加1胎撤,丟棄n個(gè)0,指數(shù)就加n断凶,并沒有損失精度伤提。
很容易得出,從1開始的連續(xù)整數(shù)里面第一個(gè)不能被float精確表示的整數(shù)认烁,其二進(jìn)制形式為:1 00000000 00000000 00000001肿男,即16777217:1.00000000 00000000 00000001×224,f有24位却嗡,最后一個(gè)1只能舍棄舶沛,也就是 1.00000000 00000000 0000000×224,即1.0×224稽穆,這個(gè)數(shù)實(shí)際上是16777216冠王。也就是說16777217和16777216的內(nèi)存表示是一樣的:
那么16777217之后的下一個(gè)可以被float精確表示的int值是多少呢?很簡(jiǎn)單舌镶,向16777217上不斷的加1柱彻,直到滿足“第一個(gè)1開始到最后一個(gè)1為止的總位數(shù)為24位”:
1 00000000 00000000 00000010就是16777218,規(guī)格化表示為:
1.00000000 00000000 0000001×224=1.00000000 00000000 0000001×2151?127
其f是23位(最后一位是1)餐胀。
2.原理
二進(jìn)制的浮點(diǎn)數(shù)哟楷,其科學(xué)記數(shù)法的形式為:
其中只能是0或1。規(guī)格化表示為:
即約定小數(shù)點(diǎn)位于最高位的1之后否灾,因此不能是0卖擅。既然整數(shù)位只能是1,那么這一位可以不用存儲(chǔ)墨技,稱之為隱含位惩阶。好處是可以多存儲(chǔ)一位小數(shù)部分,但是在作浮點(diǎn)運(yùn)算時(shí)需要特殊處理扣汪,運(yùn)算之前要補(bǔ)齊這一位断楷,運(yùn)算之后又得略去這一位,會(huì)有一點(diǎn)性能損耗崭别;如果有效位本來就足夠多冬筒,省去整數(shù)位也賺不了多少便宜,這可能是擴(kuò)展雙精度浮點(diǎn)數(shù)不采用這種方案的原因茅主。
指數(shù)n是一個(gè)普通的整數(shù)舞痰,可正可負(fù)(負(fù)指數(shù)表示純小數(shù)),在計(jì)算機(jī)科學(xué)诀姚,整數(shù)編碼一般采用補(bǔ)碼(負(fù)數(shù)是其正數(shù)的反碼+1)响牛,最高位是符號(hào)位(0表示正數(shù),1表示負(fù)數(shù)),但I(xiàn)EEE754標(biāo)準(zhǔn)中娃善,浮點(diǎn)數(shù)的指數(shù)使用了“真值 + 偏移值”的編碼方式论衍,將原碼空間分成兩部分,小的部分表示負(fù)數(shù)聚磺,大的部分表示正數(shù)坯台。一般的,全0和全1都有特殊用途瘫寝,以float為例蜒蕾,其指數(shù)部分有8位,原碼空間就是0 ~ 255焕阿,不算0和255(全0和全1)咪啡,剩下254個(gè)數(shù),對(duì)半分即127暮屡,則N位指數(shù)的編碼和原值有下列關(guān)系:
其中撤摸,E:指數(shù)編碼;e:指數(shù)真值 褒纲。float類型的指數(shù)編碼就是:E=e+127准夷,[ 1,127 ) 是負(fù)數(shù), [ 127, 254 ] 是正數(shù)莺掠,0和255有特殊用途衫嵌。
3.分類
IEEE標(biāo)準(zhǔn)定義了6類浮點(diǎn)數(shù):
指數(shù) | 分類 | 隱含位 | 尾數(shù) | 說明 |
---|---|---|---|---|
xx…xx | 有限數(shù) | 1 | xx…xx | 指數(shù)非全0,非全1彻秆,有隱含位 |
00…00 | 0 | 0 | 0 | 尾數(shù)為0 |
00…00 | 弱規(guī)范數(shù) | 無 | xx…xx | 尾數(shù)不是0 |
11…11 | ±∞ | 1 | 0 | 尾數(shù)不為0 |
11…11 | QNaN | 1 | 1xx…xx | 尾數(shù)高位為1楔绞,但尾數(shù)不為0 |
11…11 | SNaN | 1 | 0xx…xx | 尾數(shù)高位為0,但尾數(shù)不為0 |
3.1 有限數(shù)
有限數(shù)就是遵循規(guī)格化的常規(guī)數(shù)唇兑,其指數(shù)在最小數(shù)和最大數(shù)之間酒朵,且整數(shù)位恒為1,其形式為:
例如float扎附,其指數(shù)E有8位蔫耽,取值范圍為 ( 0, 255 ) 也就是 [ 1, 254 ] 。有限數(shù)在運(yùn)算過程中最常見的問題就是溢出帕棉,即運(yùn)算結(jié)果無法用有限數(shù)表示针肥。
3.2 零
0饼记,有一位隱含位(為0)香伴,除符號(hào)位可能不為0外,其它所有位都為0具则,也就是說存在正0和負(fù)0即纲,其形式為:
數(shù)學(xué)上,0沒有正負(fù)之說博肋,但按IEEE754標(biāo)準(zhǔn)低斋,卻有正負(fù)0之分(由符號(hào)位標(biāo)識(shí))蜂厅,注意:正0應(yīng)該和負(fù)0相等,而不應(yīng)正0大于負(fù)0膊畴。
3.3 弱規(guī)范數(shù)
弱規(guī)范數(shù)的指數(shù)和0一樣掘猿,都是全0,但沒有隱含位唇跨,尾數(shù)部分不為0稠通,其形式為:
弱規(guī)范數(shù)的整數(shù)位是尾數(shù)的最高位。由于弱規(guī)范數(shù)沒有隱含位买猖,在向有限數(shù)轉(zhuǎn)換時(shí)改橘,要向高位移一位,以產(chǎn)生隱含位玉控,但指數(shù)不變(不減1)飞主;從有限數(shù)形式轉(zhuǎn)換成弱規(guī)范數(shù)形式時(shí),正好相反高诺,向低位移1位碌识,指數(shù)仍不變。
在計(jì)算過程中懒叛,如果中間結(jié)果小于最小的有限數(shù)卻不是0丸冕,即出現(xiàn)下溢烹植,如果當(dāng)做0處理许饿,可能會(huì)導(dǎo)致計(jì)算終止,引入“弱規(guī)范數(shù)”之后者蠕,在0和最小的有限數(shù)之間有相當(dāng)一部分?jǐn)?shù)可以表示為“弱規(guī)范數(shù)”诅迷,從而提高了計(jì)算能力佩番。
3.4 無窮大
±∞,指數(shù)部分為全1(即最大值)罢杉,整數(shù)位是1(是隱含位)趟畏,尾數(shù)是0,其形式為:
float類型即:
產(chǎn)生∞的情形一般有:
- 自身運(yùn)算滩租,例如∞+1.0=∞
- 被0除赋秀,例如1/0=∞,1/?0=?∞
- 上溢律想,即計(jì)算結(jié)果超出了類型范圍
3.5 NaN
NaN猎莲,即Not a Number,和∞一樣技即,指數(shù)部分為全1(最大值)著洼,整數(shù)位是1(是隱含位),但尾數(shù)部分不為0,其形式為:
其中身笤,f不為0豹悬。NaN有SNaN(Signal NaN)和QNaN(Quiet NaN)之分,IEEE標(biāo)準(zhǔn)要求:SNaN參與運(yùn)算要觸發(fā)異常液荸,而QNaN則不觸發(fā)異常瞻佛。兩者的區(qū)別在于,SNaN的尾數(shù)最高位是0娇钱,而QNaN的尾數(shù)最高位是1涤久。
IEEE標(biāo)準(zhǔn)只規(guī)定NaN的尾數(shù)不為0,并沒有限定應(yīng)該是什么忍弛,這就給予具體實(shí)現(xiàn)一定的空間响迂。比如可以在計(jì)算出問題時(shí),在尾數(shù)部分設(shè)置一些特殊的值细疚,有利于調(diào)試蔗彤。
NaN有一些晦澀的運(yùn)算規(guī)則:
- 0×∞=NaN,因此“0乘任何數(shù)都是0”不是恒成立的
- NaN參與的所有邏輯運(yùn)算疯兼,只有NaN!=NaN為真然遏,其它的都不為真,即使NaN==NaN的結(jié)果也為false吧彪,因此在浮點(diǎn)數(shù)的邏輯運(yùn)算中待侵,編譯器沒有辦法做積極的優(yōu)化,因?yàn)槿绻蠳aN參與邏輯運(yùn)算姨裸,比如x=NaN秧倾,那么!(x<y)和x>=y就不等價(jià)了。
令人無語的是傀缩,編譯器不一定遵循IEEE標(biāo)準(zhǔn)的規(guī)定那先,可想而知,浮點(diǎn)運(yùn)算有多難搞赡艰。
4.特殊的數(shù)
4.1 最小的正float有限數(shù)
根據(jù)有限數(shù)的規(guī)格化形式售淡,指數(shù)取最小值1,隱含位是1慷垮,尾數(shù)取最小值0(23位都是0):
如果浮點(diǎn)運(yùn)算的結(jié)果小于這個(gè)數(shù)揖闸,就出現(xiàn)下溢,一般將其結(jié)果轉(zhuǎn)換為最小的有限數(shù)料身,或者弱規(guī)范數(shù)汤纸,如果弱規(guī)范數(shù)也不能表示,那么將轉(zhuǎn)換成0惯驼。
4.2 最大的float有限數(shù)
根據(jù)有限數(shù)的規(guī)格化形式蹲嚣,指數(shù)取最大值254,隱含位是1祟牲,尾數(shù)取最大值(23位都是1):
如果浮點(diǎn)運(yùn)算的結(jié)果超過這個(gè)數(shù)隙畜,就出現(xiàn)上溢,一般將其結(jié)果轉(zhuǎn)換為最近的有限數(shù)或∞说贝。
4.3 最小的正float弱規(guī)范數(shù)
根據(jù)弱規(guī)范數(shù)的規(guī)格化形式议惰,尾數(shù)取最小值:
f是23位,且最高位是整數(shù)位乡恕,因此小數(shù)點(diǎn)之后只有22位言询,最后一位為1,即可得出該數(shù)傲宜。
4.4 FLT_EPSILON
FLT_EPSILON是C++定義的一個(gè)float數(shù)运杭,該數(shù)是滿足1.0+FLT_EPSILON!=1.0的最小的float有限數(shù),比該數(shù)還小的float有限數(shù)會(huì)有:1.0+x=1.0函卒。根據(jù)這個(gè)定義辆憔,從比1.0大的最小float有限數(shù)開始推導(dǎo):
它的規(guī)格化表示:1.0×2e(104?127),E = 104报嵌,f = 0虱咧。注意,該數(shù)遠(yuǎn)不是最小的正float有限數(shù)锚国,它比最小的正float有限數(shù)還要“大很多”腕巡,它的指數(shù)是-23,而最小的正float有限數(shù)的指數(shù)是-126血筑。這個(gè)數(shù)并沒有什么特別的意義绘沉,在代碼中無條件的使用 fabs (x ? y) < FLT_EPSILON判斷兩個(gè)浮點(diǎn)數(shù)是否相等,可能會(huì)導(dǎo)致問題豺总。如果你的系統(tǒng)中浮點(diǎn)數(shù)很小梆砸,極端來說,甚至小于FLT_EPSILON园欣,那你用fabs (x ? y) < FLT_EPSILON來判斷相等帖世,顯然是錯(cuò)誤的,你應(yīng)該自己定義一個(gè)可以接受的誤差范圍來輔助判斷相等問題沸枯。