浮點(diǎn)數(shù) 編輯詞條 添加義項(xiàng)名浮點(diǎn)數(shù)是屬于有理數(shù)中某特定子集的數(shù)的數(shù)字表示,在計(jì)算機(jī)中用以近似表示任意某個(gè)實(shí)數(shù)。具體的說(shuō),這個(gè)實(shí)數(shù)由一個(gè)整數(shù)或定點(diǎn)數(shù)(即尾數(shù))乘以某個(gè)基數(shù)(計(jì)算機(jī)中通常是2)的整數(shù)次冪得到,這種表示方法類似于基數(shù)為10的科學(xué)計(jì)數(shù)法。 基本信息
折疊 編輯本段 浮點(diǎn)數(shù)折疊 浮點(diǎn)計(jì)算是指浮點(diǎn)數(shù)參與的運(yùn)算,這種運(yùn)算通常伴隨著因?yàn)闊o(wú)法精確表示而進(jìn)行的近似或舍入。 一個(gè)浮點(diǎn)數(shù)a由兩個(gè)數(shù)m和e來(lái)表示:a = m × b^e。在任意一個(gè)這樣的系統(tǒng)中,我們選擇一個(gè)基數(shù)b(記數(shù)系統(tǒng)的基)和精度p(即使用多少位來(lái)存儲(chǔ))。m(即尾數(shù))是形如±d.ddd...ddd的p位數(shù)(每一位是一個(gè)介于0到b-1之間的整數(shù),包括0和b-1)。如果m的第一位是非0整數(shù),m稱作規(guī)格化的。有一些描述使用一個(gè)單獨(dú)的符號(hào)位(s 代表+或者-)來(lái)表示正負(fù),這樣m必須是正的。e是指數(shù)。 折疊 結(jié)構(gòu)由此可以看出,在計(jì)算機(jī)中表示一個(gè)浮點(diǎn)數(shù),其結(jié)構(gòu)如下: 尾數(shù)部分(定點(diǎn)小數(shù)) 階碼部分(定點(diǎn)整數(shù))
這種設(shè)計(jì)可以在某個(gè)固定長(zhǎng)度的存儲(chǔ)空間內(nèi)表示定點(diǎn)數(shù)無(wú)法表示的更大范圍的數(shù)。 浮點(diǎn)加法減法運(yùn)算 設(shè)有兩個(gè)浮點(diǎn)數(shù)x和y,它們分別為 x = Mx*2^Ex y = My*2^Ey 其中Ex和Ey分別為數(shù)x和y的階碼,Mx和My為數(shù)x和y的尾數(shù)。 兩浮點(diǎn)數(shù)進(jìn)行加法和減法的運(yùn)算規(guī)則是 設(shè) Ex小于等于Ey,則 x±y = (Mx*2^(Ex-Ey)±My)*2^Ey, 完成浮點(diǎn)加減運(yùn)算的操作過(guò)程大體分為四步: 1. 0 操作數(shù)的檢查; 2. 比較階碼大小并完成對(duì)階; 3. 尾數(shù)進(jìn)行加或減運(yùn)算; 4. 結(jié)果規(guī)格化并進(jìn)行舍入處理。 ⑴ 0 操作數(shù)檢查 浮點(diǎn)加減運(yùn)算過(guò)程比定點(diǎn)運(yùn)算過(guò)程復(fù)雜。如果判知兩個(gè)操作數(shù)x或y中有一個(gè)數(shù)為0,即可得知運(yùn)算結(jié)果而沒(méi)有必要再進(jìn)行后續(xù)的一系列操作以節(jié)省運(yùn)算時(shí)間。0操作數(shù)檢查步驟則用來(lái)完成這一功能。 ⑵ 比較階碼大小并完成對(duì)階 兩浮點(diǎn)數(shù)進(jìn)行加減,首先要看兩數(shù)的階碼是否相同,即小數(shù)點(diǎn)位置是否對(duì)齊。若二數(shù)階碼相同,表示小數(shù)點(diǎn)是對(duì)齊的,就可以進(jìn)行尾數(shù)的加減運(yùn)算。反之,若二數(shù)階碼不同,表示小數(shù)點(diǎn)位置沒(méi)有對(duì)齊,此時(shí)必須使二數(shù)階碼相同,這個(gè)過(guò)程叫作對(duì)階。 要對(duì)階,首先應(yīng)求出兩數(shù)階碼Ex和Ey之差,即 △E = Ex-Ey 若△E=0,表示兩數(shù)階碼相等,即Ex=Ey;若△E>0,表示Ex>Ey;若△E<0,表示Ex<Ey。 當(dāng)Ex≠Ey 時(shí),要通過(guò)尾數(shù)的移動(dòng)以改變Ex或Ey,使之相等。原則上,既可以通過(guò)Mx移位以改變Ex來(lái)達(dá)到Ex=Ey,也可以通過(guò)My移位以改變Ey來(lái)實(shí)現(xiàn)Ex=Ey。但是,由于浮點(diǎn)表示的數(shù)多是規(guī)格化的,尾數(shù)左移會(huì)引起最高有效位的丟失,造成很大誤差。尾數(shù)右移雖引起最低有效位的丟失,但造成誤差較小。因此,對(duì)階操作規(guī)定使尾數(shù)右移,尾數(shù)右移后階碼作相應(yīng)增加,其數(shù)值保持不變。顯然,一個(gè)增加后的階碼與另一個(gè)階碼相等,增加的階碼的一定是小階。因此在對(duì)階時(shí),總是使小階向大階看齊,即小階的尾數(shù)向右移位(相當(dāng)于小數(shù)點(diǎn)左移)每右移一位,其階碼加1,直到兩數(shù)的階碼相等為止,右移的位數(shù)等于階差△E。 ⑶ 尾數(shù)求和運(yùn)算 對(duì)階結(jié)束后,即可進(jìn)行尾數(shù)的求和運(yùn)算。不論加法運(yùn)算還是減法運(yùn)算,都按加法進(jìn)行操作,其方法與定點(diǎn)加減法運(yùn)算完全一樣。 ⑷ 結(jié)果規(guī)格化 在浮點(diǎn)加減運(yùn)算時(shí),尾數(shù)求和的結(jié)果也可以得到01.ф…ф或10.ф…ф,即兩符號(hào)位不等,這在定點(diǎn)加減法運(yùn)算中稱為溢出,是不允許的。但在浮點(diǎn)運(yùn)算中,它表明尾數(shù)求和結(jié)果的絕對(duì)值大于1,向左破壞了規(guī)格化。此時(shí)將運(yùn)算結(jié)果右移以實(shí)現(xiàn)規(guī)格化表示,稱為向右規(guī)格化。規(guī)則是:尾數(shù)右移1位,階碼加1。當(dāng)尾數(shù)不是1.M時(shí)需向左規(guī)格化。 ⑸ 舍入處理 在對(duì)階或向右規(guī)格化時(shí),尾數(shù)要向右移位,這樣,被右移的尾數(shù)的低位部分會(huì)被丟掉,從而造成一定誤差,因此要進(jìn)行舍入處理。 簡(jiǎn)單的舍入方法有兩種:一種是"0舍1入"法,即如果右移時(shí)被丟掉數(shù)位的最高位為0則舍去,為1則將尾數(shù)的末位加"1"。另一種是"恒置一"法,即只要數(shù)位被移掉,就在尾數(shù)的末尾恒置"1"。 在IEEE754標(biāo)準(zhǔn)中,舍入處理提供了四種可選方法: 就近舍入其實(shí)質(zhì)就是通常所說(shuō)的"四舍五入"。例如,尾數(shù)超出規(guī)定的23位的多余位數(shù)字是10010,多余位的值超過(guò)規(guī)定的最低有效位值的一半,故最低有效位應(yīng)增1。若多余的5位 是01111,則簡(jiǎn)單的截尾即可。對(duì)多余的5位10000這種特殊情況:若最低有效位現(xiàn)為0,則截 尾;若最低有效位現(xiàn)為1,則向上進(jìn)一位使其變?yōu)?0。 朝0舍入 即朝數(shù)軸原點(diǎn)方向舍入,就是簡(jiǎn)單的截尾。無(wú)論尾數(shù)是正數(shù)還是負(fù)數(shù),截尾都使取值的絕對(duì)值比原值的絕對(duì)值小。這種方法容易導(dǎo)致誤差積累。 朝+∞舍入 對(duì)正數(shù)來(lái)說(shuō),只要多余位不全為0則向最低有效位進(jìn)1;對(duì)負(fù)數(shù)來(lái)說(shuō)則是簡(jiǎn)單的截尾。 朝-∞舍入 處理方法正好與 朝+∞舍入情況相反。對(duì)正數(shù)來(lái)說(shuō),只要多余位不全為0則簡(jiǎn)單截尾;對(duì)負(fù)數(shù)來(lái)說(shuō),向最低有效位進(jìn)1。 ⑹ 溢出處理 浮點(diǎn)數(shù)的溢出是以其階碼溢出表現(xiàn)出來(lái)的。在加\減運(yùn)算過(guò)程中要檢查是否產(chǎn)生了溢出:若階碼正常,加(減)運(yùn)算正常結(jié)束;若階碼溢出,則要進(jìn)行相應(yīng)處理。另外對(duì)尾數(shù)的溢出也需要處理。 階碼上溢 超過(guò)了階碼可能表示的最大值的正指數(shù)值,一般將其認(rèn)為是+∞和-∞。 階碼下溢 超過(guò)了階碼可能表示的最小值的負(fù)指數(shù)值,一般將其認(rèn)為是0。 尾數(shù)上溢 兩個(gè)同符號(hào)尾數(shù)相加產(chǎn)生了最高位向上的進(jìn)位,將尾數(shù)右移,階碼增1來(lái)重新對(duì)齊。 尾數(shù)下溢 在將尾數(shù)右移時(shí),尾數(shù)的最低有效位從尾數(shù)域右端流出,要進(jìn)行舍入處理。 折疊 編輯本段 實(shí)例折疊 題目例如,一個(gè)指數(shù)范圍為±4的4位十進(jìn)制浮點(diǎn)數(shù)可以用來(lái)表示43210,4.321或0.0004321,但是沒(méi)有足夠的精度來(lái)表示432.123和43212.3(必須近似為432.1和43210)。當(dāng)然,實(shí)際使用的位數(shù)通常遠(yuǎn)大于4。 折疊 特別數(shù)值此外,浮點(diǎn)數(shù)表示法通常還包括一些特別的數(shù)值:+∞和?∞(正負(fù)無(wú)窮大)以及NaN('Not a Number')。無(wú)窮大用于數(shù)太大而無(wú)法表示的時(shí)候,NaN則指示非法操作或者無(wú)法定義的結(jié)果。 折疊 二進(jìn)制表示眾所周知,計(jì)算機(jī)中的所有數(shù)據(jù)都是以二進(jìn)制表示的,浮點(diǎn)數(shù)也不例外。然而浮點(diǎn)數(shù)的二進(jìn)制表示法卻不像定點(diǎn)數(shù)那么簡(jiǎn)單了。 折疊 浮點(diǎn)數(shù)概念先澄清一個(gè)概念,浮點(diǎn)數(shù)并不一定等于小數(shù),定點(diǎn)數(shù)也并不一定就是整數(shù)。所謂浮點(diǎn)數(shù)就是小數(shù)點(diǎn)在邏輯上是不固定的,而定點(diǎn)數(shù)只能表示小數(shù)點(diǎn)固定的數(shù)值,具用浮點(diǎn)數(shù)或定點(diǎn)數(shù)表示某哪一種數(shù)要看用戶賦予了這個(gè)數(shù)的意義是什么。 C++中的浮點(diǎn)數(shù)有6種,分別是: float:單精度,32位 unsigned float:單精度無(wú)符號(hào),32位 double:雙精度,64位 long double:高雙精度,80位 然而不同的編譯器對(duì)它們的支持也略有不同,據(jù)我所知,很多編譯器都沒(méi)有按照IEEE規(guī)定的標(biāo)準(zhǔn)80位支持后兩種浮點(diǎn)數(shù)的,大多數(shù)編譯器將它們視為double,或許還有極個(gè)別的編譯器將它們視為128位?!對(duì)于128位的long double我也僅是聽(tīng)說(shuō)過(guò),沒(méi)有求證,哪位高人知道這一細(xì)節(jié)煩勞告知。 下面我僅以float(帶符號(hào),單精度,32位)類型的浮點(diǎn)數(shù)說(shuō)明C++中的浮點(diǎn)數(shù)是如何在內(nèi)存中表示的。先講一下基礎(chǔ)知識(shí),純小數(shù)的二進(jìn)制表示。(純小數(shù)就是沒(méi)有整數(shù)部分的小數(shù),講給小學(xué)沒(méi)好好學(xué)的人) 純小數(shù)要想用二進(jìn)制表示,必須先進(jìn)行規(guī)格化,即化為 1.xxxxx * ( 2 ^ n ) 的形式("^"代表乘方,2 ^ n表示2的n次方)。對(duì)于一個(gè)純小數(shù)D,求n的公式如下: n = 1 + log2(D); // 純小數(shù)求得的n必為負(fù)數(shù) 再用 D / ( 2 ^ n ) 就可以得到規(guī)格化后的小數(shù)了。接下來(lái)就是十進(jìn)制到二進(jìn)制的轉(zhuǎn)化問(wèn)題,為了更好的理解,先來(lái)看一下10進(jìn)制的純小數(shù)是怎么表示的,假設(shè)有純小數(shù)D,它小數(shù)點(diǎn)后的每一位數(shù)字按順序形成一個(gè)數(shù)列: {k1,k2,k3,...,kn} 那么D又可以這樣表示: D = k1 / (10 ^ 1 ) + k2 / (10 ^ 2 ) + k3 / (10 ^ 3 ) + ... + kn / (10 ^ n ) 推廣到二進(jìn)制中,純小數(shù)的表示法即為: D = b1 / (2 ^ 1 ) + b2 / (2 ^ 2 ) + b3 / (2 ^ 3 ) + ... + bn / (2 ^ n ) 現(xiàn)在問(wèn)題就是怎樣求得b1,b2,b3,……,bn。算法描述起來(lái)比較復(fù)雜,還是用數(shù)字來(lái)說(shuō)話吧。聲明一下,1 / ( 2 ^ n )這個(gè)數(shù)比較特殊,我稱之為位階值。 折疊 例二例如0.456,第1位,0.456小于位階值0.5故為0;第2位,0.456大于位階值0.25,該位為1,并將0.456減去0.25得0.206進(jìn)下一位;第3位,0.206大于位階值0.125,該位為1,并將0.206減去0.125得0.081進(jìn)下一位;第4位,0.081大于0.0625,為1,并將0.081減去0.0625得0.0185進(jìn)下一位;第5位0.0185小于0.03125…… 最后把計(jì)算得到的足夠多的1和0按位順序組合起來(lái),就得到了一個(gè)比較精確的用二進(jìn)制表示的純小數(shù)了,同時(shí)精度問(wèn)題也就由此產(chǎn)生,許多數(shù)都是無(wú)法在有限的n內(nèi)完全精確的表示出來(lái)的,我們只能利用更大的n值來(lái)更精確的表示這個(gè)數(shù),這就是為什么在許多領(lǐng)域,程序員都更喜歡用double而不是float。 float的內(nèi)存結(jié)構(gòu),我用一個(gè)帶位域的結(jié)構(gòu)體描述如下: struct MYFLOAT { bool bSign : 1; // 符號(hào),表示正負(fù),1位 char cExponent : 8; // 指數(shù),8位 unsigned long ulMantissa : 32; // 尾數(shù),32位 }; 符號(hào)就不用多說(shuō)了,1表示負(fù),0表示正 指數(shù)是以2為底的,范圍是 -128 到 127,實(shí)際數(shù)據(jù)中的指數(shù)是原始指數(shù)加上127得到的,如果超過(guò)了127,則從-128開(kāi)始計(jì),其行為和X86架構(gòu)的CPU處理加減法的溢出是一樣的。 比如:127 + 2 = -127;-127 - 2 = 127 尾數(shù)都省去了第1位的1,所以在還原時(shí)要先在第一位加上1。它可能包含整數(shù)和純小數(shù)兩部分,也可能只包含其中一部分,視數(shù)字大小而定。對(duì)于帶有整數(shù)部分的浮點(diǎn)數(shù),其整數(shù)的表示法有兩種,當(dāng)整數(shù)大于十進(jìn)制的16777215時(shí)使用的是科學(xué)計(jì)數(shù)法,如果小于或等于則直接采用一般的二進(jìn)制表示法。科學(xué)計(jì)數(shù)法和小數(shù)的表示法是一樣的。 小數(shù)部分則是直接使用科學(xué)計(jì)數(shù)法,但形式不是X * ( 10 ^ n ),而是X * ( 2 ^ n )。拆開(kāi)來(lái)看。 0 000000000000000000000000000000 符號(hào)位 指數(shù)位 尾數(shù)位 -------------------------------------------------------------------------------- 折疊 例三判斷兩個(gè)浮點(diǎn)數(shù)是否相等。 在這個(gè)例子中我們以C++代碼來(lái)判別兩個(gè)浮點(diǎn)數(shù)是否相等。由于浮點(diǎn)數(shù)在存儲(chǔ)中無(wú)法精確表示,所以 fp1==fp2 無(wú)法準(zhǔn)確的判斷float型變量fp1與fp2是否相等。應(yīng)該使用 (fp1-fl2)<0.0000001 來(lái)進(jìn)行判斷。 示例: bool equal(float fp1,float fp2) { if( abs( fp1 - fp2 ) < 0.00000001 ) return true; else return false; } -------------------------------------------------------------------------------- 折疊 編輯本段 導(dǎo)數(shù)字分布折疊 簡(jiǎn)介作者:concreteHAM 什么是浮點(diǎn)數(shù),不用我多說(shuō),這里我們要討論的是規(guī)格化的任意進(jìn)制浮點(diǎn)數(shù)的前導(dǎo)數(shù)字的概率分布。 在《計(jì)算機(jī)程序設(shè)計(jì)藝術(shù)》第二卷中做了非常深入的討論,這里我從中精煉出要點(diǎn)。 折疊 實(shí)例例如: 2.345 E 67 就只有一個(gè)"隨機(jī)"的浮點(diǎn)數(shù)而言,討論其分布式?jīng)]有意義的,我們要討論的是充分多個(gè)"隨機(jī)"數(shù)進(jìn)行的一系列運(yùn)算后產(chǎn)生的浮點(diǎn)結(jié)果的前導(dǎo)數(shù)字分布。 假設(shè)現(xiàn)在有一巨大的浮點(diǎn)數(shù)集,依此對(duì)數(shù)集中每個(gè)浮點(diǎn)數(shù)都乘以2,其中有一個(gè)十進(jìn)制浮點(diǎn)數(shù)F,它的前導(dǎo)數(shù)字是1,那么它底數(shù)可能的值范圍就是1.000…~1.999…,乘以一個(gè)數(shù)字2,那么它的底數(shù)就變成2.000…~3.999…,很明顯乘以2以前前導(dǎo)數(shù)字是1的浮點(diǎn)個(gè)數(shù)與現(xiàn)在前導(dǎo)數(shù)字是2、3的浮點(diǎn)個(gè)數(shù)相同。以此我們接下來(lái)分析。 對(duì)于一個(gè)b進(jìn)制的浮點(diǎn)數(shù),它的前導(dǎo)數(shù)字x范圍就是0 < x < b,設(shè)f(x)是上述數(shù)集的前導(dǎo)數(shù)字的概率密度函數(shù)(注:是密度函數(shù)),那么它在前導(dǎo)數(shù)字u和v之間(0<u<v<b)的概率就是: ∫[u,v]f(x)dx ⑴ 由前面所述的,對(duì)于一個(gè)充分小增量Δx,f(x)必須滿足這樣一個(gè)公式: f⑴Δx = x*f(x)Δx ⑵ 因?yàn)? f⑴Δx是f⑴微分段內(nèi)的概率,根據(jù)前面所述,f⑴Δx概率等于f(1*x)*(x*Δx) 很明顯: f(x) = f⑴/x ⑶ 兩邊在[1,b]之間進(jìn)行積分,等號(hào)左邊必定為1,右邊等于f⑴ln(b): 1 = f⑴ln(b) ⑷ 得:f⑴ = 1/ln(b) 帶入⑶中: f(x) = 1/(x*ln(b)) 那么利用⑴式得: ∫[u,v]1/(x*ln(b))dx = ln(v/u) / ln(b) ⑸ 這就是求前導(dǎo)數(shù)字的概率分布函數(shù)。 例如b = 10進(jìn)制時(shí),前導(dǎo)數(shù)字為1的概率就是: = ln((1+1)/1) / ln⑽ ≈ 0.301 前導(dǎo)數(shù)字為9的概率就是: = ln((9+1)/9) / ln⑽ ≈0.0458 以下是一個(gè)測(cè)試程序(Mathematica軟件): T[n_,b_]:=Block[{res={},ran,i,a}, For[i=1,i<b,i++; res=Append[res,0] ]; For[i=0,i<n,i++; ran=Random[]*Random[]*Random[]; 充分打亂浮點(diǎn)數(shù) ran=Log[b,ran]; a=Floor[b^(ran-Floor[ran])]; 取出前導(dǎo)數(shù)字 res[[a]]++ 對(duì)前導(dǎo)數(shù)字個(gè)數(shù)統(tǒng)計(jì) ]; Return[res] ] 執(zhí)行T[100000,10],以10進(jìn)制測(cè)試100000個(gè)浮點(diǎn)數(shù),得到一個(gè)分布: {30149,18821,13317,9674,7688,6256,5306,4655,4134} 和理論值相當(dāng)接近。 展開(kāi) |
|
來(lái)自: 超越夢(mèng)想之上 > 《電腦知識(shí)》