當(dāng)前位置:工程項(xiàng)目OA系統(tǒng) > 領(lǐng)域應(yīng)用 > 倉(cāng)庫(kù)進(jìn)銷存管理系統(tǒng) > 入庫(kù)出庫(kù)管理軟件
【CC++語(yǔ)言入門篇】-- 剖析浮點(diǎn)數(shù)
申請(qǐng)免費(fèi)試用、咨詢電話:400-8352-114
在本模塊的第三篇就已經(jīng)講了基本的數(shù)據(jù)類型,其中把浮點(diǎn)數(shù)刻意留在了后面來(lái)介紹。我的理解是在我們理解了內(nèi)存,指針,位運(yùn)算等后,再來(lái)介紹浮點(diǎn)這個(gè)特殊而又普通的數(shù)據(jù)類型比較好理解。浮點(diǎn)數(shù)和基本類型數(shù)據(jù)的存儲(chǔ)差別比較大,這里不是說(shuō)存儲(chǔ)形式的差別,而是浮點(diǎn)數(shù)存放的時(shí)候是要經(jīng)過(guò)運(yùn)算后再轉(zhuǎn)換成整數(shù)的4字節(jié)或8字節(jié)的形式,然后再存放到內(nèi)存里。因此,只通過(guò)16進(jìn)制數(shù)是看不出來(lái)和整數(shù)有什么差別。同樣,浮點(diǎn)數(shù)具體是怎么存儲(chǔ)的,在大學(xué)的課程上一般不會(huì)細(xì)細(xì)講解,一般是我們自己有興趣再查閱資料。包括本篇的內(nèi)容,如果你不是一個(gè)自學(xué)者或者充滿好奇心,你也不會(huì)看下去,也不會(huì)找到本篇的URL。因此,包括很多已經(jīng)工作很多年的程序員都不知道浮點(diǎn)數(shù)具體是怎么運(yùn)算然后存儲(chǔ)的。就我來(lái)講,認(rèn)為還是非常有必要了解這個(gè)常用的數(shù)據(jù)類型的換算過(guò)程,雖然我們個(gè)人來(lái)講很難去打破當(dāng)前浮點(diǎn)數(shù)的計(jì)算規(guī)則以至于將他的精度提高,但是了解下底層工作者們的辛苦,我們應(yīng)該向他們真誠(chéng)的致敬。因?yàn)橛兴麄?,我們便有了大樹可以乘涼?/FONT>
好了,廢話不多說(shuō)。本篇的目的就是為了讓更多的人了解浮點(diǎn)數(shù)存儲(chǔ)的基本原理,還是那句話,學(xué)習(xí)的同時(shí)帶著思考。同樣這里不討論浮點(diǎn)數(shù)的精度損失和數(shù)值的計(jì)算理論。直接講實(shí)質(zhì)的表現(xiàn)。
在計(jì)算機(jī)發(fā)展過(guò)程中,我們使用的小數(shù)和實(shí)數(shù)曾經(jīng)提出過(guò)很多種的表示方法。典型的比如相對(duì)于浮點(diǎn)數(shù)的定點(diǎn)數(shù)(Fixed Point Number)。在這種表達(dá)方式中,小數(shù)點(diǎn)固定的位于實(shí)數(shù)所有數(shù)字中間的某個(gè)位置。貨幣的表達(dá)就可以使用這種方式,比如 88.22 或者 22.88 可以用于表達(dá)具有四位精度(Precision),小數(shù)點(diǎn)后有兩位的貨幣值。由于小數(shù)點(diǎn)位置固定,所以可以直接用四位數(shù)值來(lái)表達(dá)相應(yīng)的數(shù)值。SQL 中的 NUMBER 數(shù)據(jù)類型就是利用定點(diǎn)數(shù)來(lái)定義的。還有一種提議的表達(dá)方式為有理數(shù)表達(dá)方式,即用兩個(gè)整數(shù)的比值來(lái)表達(dá)實(shí)數(shù)。
很顯然,上面的定點(diǎn)數(shù)表示法有缺陷,不能表示很小的數(shù)或者很大的數(shù)。于是,為了解決這種問(wèn)題,我們的前輩們自然想到了科學(xué)技術(shù)法的形式來(lái)表示,即用一個(gè)尾數(shù)(Mantissa ),一個(gè)基數(shù)(Base),一個(gè)指數(shù)(Exponent)以及一個(gè)表示正負(fù)的符號(hào)來(lái)表達(dá)實(shí)數(shù)。比如 123.456 用十進(jìn)制科學(xué)計(jì)數(shù)法可以表達(dá)為 1.23456 × 102 ,其中 1.23456 為尾數(shù),10 為基數(shù),2 為指數(shù)。浮點(diǎn)數(shù)利用指數(shù)達(dá)到了浮動(dòng)小數(shù)點(diǎn)的效果,從而可以靈活地表達(dá)更大范圍的實(shí)數(shù)。
大約就在1985年,IEEE標(biāo)準(zhǔn)754的推出,它是一個(gè)仔細(xì)制定的表示浮點(diǎn)數(shù)及其運(yùn)算的標(biāo)準(zhǔn)。這項(xiàng)工作是從1976年Intel發(fā)起8087的設(shè)計(jì)開始的,8087是一種為8086處理器提供浮點(diǎn)支持的芯片,他們雇傭了William Kahan,加州大學(xué)伯克利分校的一位教授,作為幫助設(shè)計(jì)未來(lái)處理器浮點(diǎn)標(biāo)準(zhǔn)的顧問(wèn)。他們支持Kahan加入一個(gè)IEEE資助的制訂工業(yè)標(biāo)準(zhǔn)的委員會(huì)。這個(gè)委員會(huì)最終采納了一個(gè)非常接近于Kahan為Intel設(shè)計(jì)的標(biāo)準(zhǔn)。目前,實(shí)際上所有的計(jì)算機(jī)夠支持這個(gè)后來(lái)被稱為IEEE浮點(diǎn)(IEEE floating point)的標(biāo)準(zhǔn)。這大大改善了科學(xué)應(yīng)用程序在不同機(jī)器上的可移植性。所謂IEEE就是電器和電子工程師協(xié)會(huì)。
介紹完了歷史,先來(lái)看看浮點(diǎn)數(shù)最直接的表示。在數(shù)學(xué)上:
12.341010 = 1*101 + 2*100 + 3*10-1 + 4*10-2 = 12(34/100) (這里由于編輯器的原因,只能寫這么機(jī)械了)。
在比如二進(jìn)制:
101.112 = 1*22 + 0*21 + 1*20 + 1*2-1 + 1*2-2 = 4 + 0 + 1 + 1/2 + 1/4 = 5(3/4)。
上面簡(jiǎn)單的描述了在數(shù)學(xué)意義上的浮點(diǎn)數(shù)表示,但是在計(jì)算機(jī)中,我們存放在內(nèi)存中的直觀上看16進(jìn)制數(shù),那么這些16進(jìn)制數(shù)是怎么表示我們浮點(diǎn)數(shù)的二進(jìn)制形式呢?
在 IEEE 標(biāo)準(zhǔn)中,浮點(diǎn)數(shù)是將特定長(zhǎng)度的連續(xù)字節(jié)的所有二進(jìn)制位分割為特定寬度的符號(hào)域,指數(shù)域和尾數(shù)域三個(gè)域,其中保存的值分別用于表示給定二進(jìn)制浮點(diǎn)數(shù)中的符號(hào),指數(shù)和尾數(shù)。這樣,通過(guò)尾數(shù)和可以調(diào)節(jié)的指數(shù)(所以稱為"浮點(diǎn)")就可以表達(dá)給定的數(shù)值了。具體的格式:
符號(hào)位 階碼 尾數(shù) 長(zhǎng)度
float 1 8 23 32
double 1 11 52 64
我們都知道浮點(diǎn)數(shù)在32位機(jī)子上有兩種精度,float占32位,double占64位。很多朋友喜歡把double用于8字節(jié)的數(shù)據(jù)存儲(chǔ)。從這點(diǎn)我們應(yīng)該不要特殊看到浮點(diǎn)數(shù)的內(nèi)存存儲(chǔ)形式,他跟整數(shù)沒(méi)有什么區(qū)別,只是在這4字節(jié)或者8字節(jié)里有3個(gè)區(qū)域,整數(shù)有符號(hào)只有符號(hào)位及后面的數(shù)值,之所以最高位表示有符號(hào)數(shù)的符號(hào)位。原因之一在于0x7fffffff位最大整數(shù),為整個(gè)32位所能表示的最大無(wú)符號(hào)整數(shù)0xffffffff的一半減一,也就是:比如1字節(jié):無(wú)符號(hào)是:0xff,有符號(hào)正數(shù)為:(0, 127],負(fù)數(shù)為[-128, 0)。在8位有符號(hào)時(shí),肯定內(nèi)存值大于等于: 0x80。二進(jìn)制就是1000 0000,比他大,只會(huì)在低7位上變化,最高位已經(jīng)是1了,變了就變小了。所以這里也是一個(gè)比較巧用的地方,一舉兩得。
那么,我們先來(lái)看32位浮點(diǎn)數(shù) 的換算:
1. 從浮點(diǎn)數(shù)到16進(jìn)制數(shù)
float var = 5.2f;
就這個(gè)浮點(diǎn)數(shù),我們一步一步將它轉(zhuǎn)換為16進(jìn)制數(shù)。
首先,整數(shù)部分5,4位二進(jìn)制表示為:0101。
其次,小數(shù)部分0.2,我們應(yīng)該學(xué)了小數(shù)轉(zhuǎn)換為二進(jìn)制的計(jì)算方法,那么就是依次乘以2,取整數(shù)部分作為二進(jìn)制數(shù),取小數(shù)部分繼續(xù)乘以2,一直算到小數(shù)結(jié)果為0為止。那么對(duì)0.2進(jìn)行計(jì)算:
0.2*2 = 0.4 * 2 = 0.8 * 2 = 1.6(0.6) * 2 = 1.2(0.2)*2 = 0.4 * 2 = 0.8 * 2 = 1.6(0.6) * 2 = 1.2 ... ...
0 0 1 1 0 0 1 1 ... ...
因此,這里把0.2的二進(jìn)制就計(jì)算出來(lái)了,結(jié)果就為:0.00110011... ... 這里的省略號(hào)是你沒(méi)有辦法計(jì)算完。二進(jìn)制序列無(wú)限循環(huán),沒(méi)有到達(dá)結(jié)果為0的那一天。那么此時(shí)我們?cè)撛趺崔k?這里就得取到一定的二進(jìn)制位數(shù)后停止計(jì)算,然后舍入。我們知道,float是32位,后面尾數(shù)的長(zhǎng)度只能最大23位。因此,計(jì)算結(jié)束的時(shí)候,整數(shù)部分加上小數(shù)部分的二進(jìn)制一共23位二進(jìn)制。因此5.2的二進(jìn)制表示就為:
101.00110011001100110011
一共23位。
此時(shí),使用科學(xué)計(jì)數(shù)法表示,結(jié)果為:
1.0100110011001100110011 * 22
由于我們規(guī)定,使用二進(jìn)制科學(xué)計(jì)數(shù)法后,小數(shù)點(diǎn)左邊必須為1(肯定為1嘛,為0的話那不就是0.xxxx*sxxx 了,這樣沒(méi)有什么意義),這里不能為0是有一個(gè)很大的好處的,為什么?因?yàn)橐?guī)定為1,這樣這個(gè)1就不用存儲(chǔ)了,我們?cè)趶?6進(jìn)制數(shù)換算到浮點(diǎn)數(shù)的時(shí)候加上這個(gè)1就是了,因?yàn)槲覀冎肋@里應(yīng)該有個(gè)1,省略到這個(gè)1的目的是為了后面的小數(shù)部分能夠多表示一位,精度就更高一些了喲。那么省略到小數(shù)點(diǎn)前面的1后的結(jié)果為:
.01001100110011001100110 * 22
這里后面藍(lán)色的0就是補(bǔ)上的,這里不是隨便補(bǔ)的一個(gè)0,而是0.2的二進(jìn)制在這一位上本來(lái)就應(yīng)該為0,如果該為1,我們就得補(bǔ)上一個(gè)1.是不是這樣多了一位后,實(shí)際上我們用23位表示了24位的數(shù)據(jù)量。有一個(gè)位是隱藏了,固定為1的。我們不必記錄它。
但是,在對(duì)階或向右規(guī)格化時(shí),尾數(shù)要向右移位,這樣被右移的尾數(shù)的低位部分會(huì)被丟掉,從而造成一定的誤差,因此要進(jìn)行舍入處理。 常用的舍入方法有兩種:一種是“0舍1入”法,即如果右移時(shí)被丟掉數(shù)位的最高位為0則舍去,為1則將尾數(shù)的末位加“1”,另一種是“恒置1”,即只要數(shù)位被移掉,就在尾數(shù)的末位恒置“1”。
舉個(gè)例子:
123.456的二進(jìn)制表示:
123.456的二進(jìn)制到23位時(shí):111 1011.0111 0100 1011 1100 01...
后面還有依次為01...等低位,由于最高位的1會(huì)被隱藏,向后擴(kuò)展一位如果不做舍入操作則結(jié)果為:
1.11 1011 0111 0100 1011 1100 0 * 26
但是經(jīng)過(guò)舍入操作后,由于被舍掉的位的最高位是1,或者“恒置1”法,最后面的0都應(yīng)該是1。因此最終就應(yīng)該是:
1.11 1011 0111 0100 1011 1100 1 * 26
在這里需要說(shuō)明,不管是恒置1,還是0舍1入法,其根本都是為了減小誤差。
好了,尾數(shù)在這里就計(jì)算好了,他就是 01001100110011001100110 。
再來(lái)看階數(shù),這里我們知道是2^2次方,那么指數(shù)就是2。同樣IEEE標(biāo)準(zhǔn)又規(guī)定了,因?yàn)橹虚g的 階碼在float中是占8位,而這個(gè) 階碼又是有符號(hào)的(意思就是說(shuō),可以有2^-2次方的形式)。
float 類型的 偏置量 Bias = 2k-1 -1 = 28-1 -1 = 127 ,但還要補(bǔ)上剛才因?yàn)樽笠谱鳛樾?shù)部分的 2 位(也就是科學(xué)技術(shù)法的指數(shù)),因此偏置量為 127 + 2=129 ,就是 IEEE 浮點(diǎn)數(shù)表示標(biāo)準(zhǔn):
V = (-1)s × M × 2E
E = e - Bias
中的 e ,此前計(jì)算 Bias=127 ,剛好驗(yàn)證了 E = 129 - 127 = 2 。
這里的階碼就是12910 ,二進(jìn)制就是:1000 00012 。
因此,拼接起來(lái)后:
1000 0001 01001100110011001100110
| ← 8位 → | | ←------------- 23位 -------------→ |
一共就是31位了,這里還差一位,那就是符號(hào)位,我們定義的是5.2,正數(shù)。因此這里最高位是0,1表示負(fù)數(shù)。
而后結(jié)果就是:
0 1000 0001 01001100110011001100110
1位 | ← 8位 → | | ←-------------- 23位 ------------→ |
到這里,我們內(nèi)存里面的十六進(jìn)制數(shù)產(chǎn)生了,分開來(lái)看:
0 100 0000 1 010 0110 0110 0110 0110 0110
4 0 A 6 6 6 6 6
因此,我們看到的就是0x40A66666, 此就是5.2最終的整數(shù)形式。
2.從十六進(jìn)制數(shù)到浮點(diǎn)數(shù)
我們還是可以用上面5.2的例子,再將0x40A66666換算回去,用同樣一個(gè)例子,結(jié)果更直觀,逆運(yùn)算更好理解。那我們就開始吧。
首先,要還原回去,必須將這個(gè)16進(jìn)制用我們的計(jì)算器換算成二進(jìn)制:
0 100 0000 1 010 0110 0110 0110 0110 011 0
我是COPY上面的。這里顏色已經(jīng)很明顯了,我劃分成了3個(gè)區(qū)域 。
首先確定符號(hào),這里是0,因此是正數(shù)。
其次看綠色的8位,換成10進(jìn)制就是:12910
我們逆運(yùn)算,知道這里需要129 - 127 = 2得到指數(shù),得到了指數(shù),我們便知道我們小數(shù)點(diǎn)是向哪個(gè)方向移動(dòng)了好多位。腦子里已經(jīng)有了一個(gè)科學(xué)計(jì)數(shù)法的錐形。
再次把紅色的23位提取出來(lái),這里不把它換成10進(jìn)制,因?yàn)槲覀冎笖?shù)是表示的二進(jìn)制上移動(dòng)了多少位,底數(shù)是2,而不是10。
這里因?yàn)橹拔覀兌贾烙袀€(gè)固定的1給省略了,因此這里要給加上去。加上去之后:
1 010 0110 0110 0110 0110 011 0
這里是24位,我們先不管,小數(shù)點(diǎn)添進(jìn)去:
1 . 010 0110 0110 0110 0110 011 0 * 22
然后將科學(xué)計(jì)數(shù)法變換成普通的二進(jìn)制小數(shù):
1 01 . 0 0110 0110 0110 0110 011 0
到這里,就真正可以把整數(shù)部分換成十進(jìn)制了:
1 01 . 0 0110 0110 0110 0110 011 0
5. xxxxxxxxxxxxxxxxxxxxxxxxxxxxx
我們知道了,整數(shù)部分是5,后面的小數(shù)部分再進(jìn)行逆運(yùn)算:
這里我們就應(yīng)該想想小數(shù)到二進(jìn)制數(shù)是乘法,這里逆運(yùn)算就應(yīng)該除以2,因此就可以表示為:
0 . 0 0110 0110 0110 0110 011 0
0 + 0*2-1 + 0*2-2 + 1*2-3 + 1*2-4 + 0*2-5 + 0*2-6 + 1*2-7 + ... ... + 0*2-21 這樣一個(gè)式子,我們算出結(jié)果來(lái),放在浮點(diǎn)數(shù)里:
5.1999998。
因此我們可以看到精度已經(jīng)有損失了。
問(wèn)題一:寫寫-5.2的16進(jìn)制數(shù)?
再來(lái)看一個(gè)例子:
float var = 0.5, 算16進(jìn)制數(shù)。
首先,0.5整數(shù)部分為0,這里就不處理了。
其次,0.5小數(shù)部分,二進(jìn)制表示為:0.1
這里是0.1,將尾數(shù)補(bǔ)滿23位則是:
0.10 0000 0000 0000 0000 0002
由于小數(shù)點(diǎn)左邊是0,因此需要向右移動(dòng)一位 ,因此:
1.0 0000 0000 0000 0000 00002 * 2-1
這里1又被省略掉,所以23位全部變成了0 ,因此:
.00 0000 0000 0000 0000 00002 * 2-1
然后,因?yàn)檫@里指數(shù)是-1,因此階碼就是:-1 + 127 = 126 = 0111 11102
這樣一來(lái),階碼就有了,由于又是正數(shù),那么組合起來(lái):
0 01111110 00000000000000000000000
這樣一來(lái),最終的16進(jìn)制數(shù)則為:0x3f000000.
是不是很簡(jiǎn)單啊。
64位浮點(diǎn)數(shù) 的換算:
這里就不再具體說(shuō)明怎么換算的了,只需要提到2個(gè)地方:
一是,中間的階碼在double中占有11位,因此就不是+127了,而是加上1023,因?yàn)?1位能表示的最大無(wú)符號(hào)數(shù)是2047,因此有符號(hào)范圍[-1024, 1023]。
二是,尾數(shù)是52位,因此精度更高,能表示的數(shù)也就越大。我們?cè)趽Q算5.2的時(shí)候,后面的小數(shù)二進(jìn)制+前面的5的二進(jìn)制再省略一位后的總位數(shù)要填滿52位。
好了,浮點(diǎn)數(shù)也沒(méi)有太多要說(shuō)的,就到這里吧,在用的時(shí)候注意精度和范圍就可以了。
最后在提一個(gè)問(wèn)題:
問(wèn)題二:
float var0 = 5.2;
float var1 = 500.2;
float var2 = 50000.2;
float var3 = 5000000.2;
觀察這幾個(gè)數(shù),加深一下那三個(gè)域的計(jì)算方式,并說(shuō)出這些數(shù)據(jù)有什么規(guī)律?
- 1C++為什么難學(xué)
- 2如何重新安裝IE瀏覽器
- 3怎樣隱藏共享資源
- 4計(jì)算機(jī)不能連接網(wǎng)絡(luò),該怎么檢查
- 5突然死機(jī)的原因
- 6更改系統(tǒng)源文件途徑
- 7應(yīng)用程序不能啟動(dòng)
- 8IE無(wú)法打開二級(jí)鏈接
- 9如何多系統(tǒng)安裝
- 10三招搞惦MySQL數(shù)據(jù)庫(kù)性能優(yōu)化
- 11舉例說(shuō)明使用MATLAB Coder從MATLAB生成C/C++代碼步驟
- 12操作系統(tǒng)進(jìn)程描述
- 13在DOS下安裝Win XP
- 14常遇電腦故障應(yīng)急處理方法
- 15入庫(kù)出庫(kù)工程管理軟件免費(fèi)下載?公司怎么樣?
- 16什么是信使服務(wù)
- 17硬盤引導(dǎo)型故障分析及排除
- 18雙擊無(wú)法打開文件夾
- 19解決鼠標(biāo)亂動(dòng)問(wèn)題
- 20如何隱藏磁盤驅(qū)動(dòng)器
- 21Explorer.exe程序在系統(tǒng)中的作用
- 22更改IE的默認(rèn)搜索引擎
- 23如何使用搜索技巧來(lái)成為一名高效的程序員
- 24RUNDLL32.EXE 是什么程序
- 25怎樣設(shè)置一些常用軟件通過(guò)代理上網(wǎng)
- 26QQ密碼遺忘了怎么辦
- 27解決無(wú)法關(guān)機(jī)問(wèn)題
- 28黑屏的幾個(gè)原因,為什么電腦老是黑屏
- 29網(wǎng)絡(luò)不通該怎么解決
- 30更改臨時(shí)文件夾的路徑
成都公司:成都市成華區(qū)建設(shè)南路160號(hào)1層9號(hào)
重慶公司:重慶市江北區(qū)紅旗河溝華創(chuàng)商務(wù)大廈18樓