Hopes

          Start Here..

           

          浮點(diǎn)型數(shù)據(jù)存儲(chǔ)方式分析(轉(zhuǎn))

           

          分類(lèi): C/C++

           

              筆試面試的過(guò)程中難免會(huì)遇到很多自己不清楚或者是當(dāng)時(shí)不清楚的問(wèn)題,關(guān)于浮點(diǎn)型數(shù)據(jù)的問(wèn)題我認(rèn)為是比較難的問(wèn)題啦,如果不清楚浮點(diǎn)型存儲(chǔ)問(wèn)題,回答問(wèn)題只有猜結(jié)果了。
             
              在面試的過(guò)程中一般面試官也很少問(wèn)關(guān)于浮點(diǎn)數(shù)的問(wèn)題,因?yàn)楦↑c(diǎn)數(shù)對(duì)于很多的處理器是不支持的,對(duì)于上層應(yīng)用的程序員而言,存儲(chǔ)類(lèi)型的問(wèn)題都會(huì)考慮,但是對(duì)于嵌入式程序員掌握內(nèi)存的分配情況是必須的,只有這樣才能把握基本的操作,關(guān)于浮點(diǎn)型數(shù)據(jù),在C語(yǔ)言中存在兩種類(lèi)型的浮點(diǎn)型,其中一種是單精度的浮點(diǎn)型(float)、雙精度的浮點(diǎn)型(double)。浮點(diǎn)型數(shù)據(jù)和一般的整形數(shù)據(jù)具有較大的差別,具體的差別就是浮點(diǎn)型數(shù)據(jù)是一個(gè)近似值,并不是一個(gè)準(zhǔn)確的值,這也就是為什么在比較浮點(diǎn)型數(shù)據(jù)的大小時(shí)不能采用直接等于的方式進(jìn)行比較,因?yàn)楦↑c(diǎn)型只是一個(gè)近似值,也就是一個(gè)值可能表示一個(gè)范圍區(qū)間,這樣的表達(dá)方式就使得對(duì)浮點(diǎn)型采用相等的判斷方法進(jìn)行判斷可能不合理,只有通過(guò)比較一個(gè)數(shù)是否在這個(gè)小的范圍內(nèi),在這個(gè)小的范圍內(nèi)說(shuō)明這個(gè)數(shù)可以表述這個(gè)范圍內(nèi)的數(shù)據(jù)。因此在計(jì)算值比較兩個(gè)浮點(diǎn)數(shù)變量不能通過(guò)做差是否等于零來(lái)判斷。而只能通過(guò)如下的方式判斷:

          點(diǎn)擊(此處)折疊或打開(kāi)

          1. const float ESPSION = 0.000001;
          2. if((x-y)>=-ESPSION && (x-y)<= ESPSION)
              這種實(shí)現(xiàn)方式是基本的比較方式,這種判讀方法剛好就是判斷變量是否處于一個(gè)范圍內(nèi),這里的范圍是-0.000001<x<0.000001。
             記得在網(wǎng)上關(guān)于浮點(diǎn)型的一道題目如下:

          點(diǎn)擊(此處)折疊或打開(kāi)

          1. typedef union test
          2. {
          3.     float a;
          4.     int i;
          5.     char c[4];
          6. }Test;

          7. int main()
          8. {
          9.     Test t;
          10.     t.a = 5.0;
          11.     printf("%f\n",t.a)
          12.     printf("%d\n",t.i);
          13.     printf("%c,%c,%c,%c\n",t.c[3],t.c[2],t.c[1],t.c[0]);
          14.     return 0;
          15. }
              這個(gè)題剛開(kāi)始看的時(shí)候我并不知道其中有什么問(wèn)題,我認(rèn)為就是簡(jiǎn)單的數(shù)值強(qiáng)制類(lèi)型轉(zhuǎn)換,但是后面我發(fā)現(xiàn)并不是強(qiáng)制類(lèi)型轉(zhuǎn)換,這個(gè)題只有搞清楚了數(shù)據(jù)的存儲(chǔ)方式才能真正的清楚輸出的結(jié)果。
              其實(shí)關(guān)于數(shù)據(jù)類(lèi)型的存儲(chǔ)方式問(wèn)題的題目類(lèi)型很多,其中比較常見(jiàn)的有:1、存儲(chǔ)空間大小問(wèn)題,即字節(jié)對(duì)齊問(wèn)題,這種問(wèn)題一般要求我們對(duì)基本類(lèi)型了解其中的基本原理,相對(duì)來(lái)說(shuō)比較簡(jiǎn)單。2、字符串的長(zhǎng)度問(wèn)題,這種問(wèn)題主要是通過(guò)sizeof,strlen這兩個(gè)不同的函數(shù)來(lái)比較。3、還有一類(lèi)問(wèn)題就是采用printf函數(shù)實(shí)現(xiàn)數(shù)據(jù)類(lèi)型的轉(zhuǎn)換問(wèn)題。這種轉(zhuǎn)化問(wèn)題是在printf的格式中體現(xiàn)出來(lái)的,是各種關(guān)于內(nèi)存分布最容易失誤的地方,為什么說(shuō)最容易失誤呢,因?yàn)椴煌膮?shù)就有可能產(chǎn)生不一樣的輸出結(jié)果,說(shuō)白了這種題很容易出錯(cuò)是因?yàn)槲覀兒苌僮⒁膺@些問(wèn)題。
             
              上面的這個(gè)題返回值非常的特殊,為什么會(huì)特殊呢?我就通過(guò)浮點(diǎn)數(shù)的基本存儲(chǔ)方式來(lái)說(shuō)明。在32位系統(tǒng)中,float類(lèi)型占有4個(gè)bytes,double則占有8個(gè)bytes。對(duì)于整形數(shù)據(jù)而言,我們很容易就知道數(shù)據(jù)是順序存儲(chǔ)的,雖然有大小端之分,但是基本的準(zhǔn)則我們認(rèn)為是按照順序存儲(chǔ)的。但是float和double型卻是比較特殊的數(shù)據(jù)類(lèi)型,因?yàn)檫@兩種數(shù)據(jù)類(lèi)型并不是按照整形數(shù)據(jù)的順序存儲(chǔ)方式進(jìn)行存儲(chǔ)的,而是按照一定的標(biāo)志IEEE來(lái)實(shí)現(xiàn)的,都有各自的標(biāo)志編碼方式:
           

              浮點(diǎn)型變量在計(jì)算機(jī)內(nèi)存中占用4字節(jié)(Byte,32-bit。遵循IEEE-754格式標(biāo)準(zhǔn)。一個(gè)浮點(diǎn)數(shù)由2部分組成:底數(shù)m 指數(shù)e

                                    ±mantissa × 2^exponent

          (注意,公式中的mantissa exponent使用二進(jìn)制表示)

                               (具體可參看深入理解計(jì)算機(jī)系統(tǒng))

           

              底數(shù)部分 使用2進(jìn)制數(shù)來(lái)表示此浮點(diǎn)數(shù)的實(shí)際值。

              指數(shù)部分 占用8-bit的二進(jìn)制數(shù),可表示數(shù)值范圍為0255。但是指數(shù)應(yīng)可正可負(fù),所以IEEE規(guī)定,此處算出的次方(即是來(lái)自?xún)?nèi)存存儲(chǔ)的內(nèi)容,存儲(chǔ)指數(shù))須減去127才是真正的指數(shù)(實(shí)際的指數(shù),如12.5轉(zhuǎn)換為二進(jìn)制為:1100.100=1.100100*23, 3即為實(shí)際指數(shù))。所以float的指數(shù)可從 -126128.

                  底數(shù)部分實(shí)際是占用24-bit的一個(gè)值,由于其最高位始終為1,所以最高位省去不存儲(chǔ),在存儲(chǔ)中只有23-bit到目前為止,底數(shù)部分23位加上指數(shù)部分8 使用31位。那么前面說(shuō)過(guò),float是占用4個(gè)字節(jié)即32-bit, 那么還有一位是干嘛用的呢?  還有一位,其實(shí)就是4字節(jié)中的最高位,用來(lái)指示浮點(diǎn)數(shù)的正負(fù),當(dāng)最高位是1時(shí),為負(fù)數(shù),最高位是0時(shí),為正數(shù)。

           

          也就是說(shuō)我們可以認(rèn)為float在小端CPU的編碼方式應(yīng)該是:

          31<-------------------------------------------------0

          S(1bit)| E(8bits)|           M(23bits)              |

           

          即:

          -----------------------------------------------------
          ADDR0+3         ADDR0+2        ADDR0+1         ADDR0

          SEEEEEEE        EMMMMMMM       MMMMMMMM     MMMMMMMM

          -----------------------------------------------------

                S: 表示浮點(diǎn)數(shù)正負(fù),1為負(fù)數(shù),0為正數(shù)。

                E: 指數(shù)加上127后的值的二進(jìn)制數(shù)

                M: 24-bit的底數(shù)(只存儲(chǔ)23-bit

          需要注意,浮點(diǎn)數(shù)0時(shí),指數(shù)和底數(shù)都為0,但此前的公式不成立。因?yàn)?/span>20次方為1,所以,0是個(gè)特例。當(dāng)然,這個(gè)特例也不用認(rèn)為去干擾,編譯器會(huì)自動(dòng)去識(shí)別。

           

             這樣我們就可以知道前面這個(gè)題中輸出的結(jié)果啦,從上面的分析可以知道一個(gè)float型的數(shù)的基本存儲(chǔ)方式,按照上面的實(shí)現(xiàn)可以知道5.0的存儲(chǔ)方式為如下的形式:

                                    01000000101000000000000000000000

             從上面的這個(gè)存儲(chǔ)形式我們可以得到基本的整形數(shù)值時(shí)0x40A00000。進(jìn)而也就知道了4個(gè)bytes中的數(shù)值大小。

            

             這是充分利用了聯(lián)合體的共享內(nèi)存特性,我們改變程序如下所示:

          點(diǎn)擊(此處)折疊或打開(kāi)

          1. #include<stdio.h>

          2. typedef union test
          3. {
          4.     float a;
          5.     int i;
          6.     char c[4];
          7. }Test;

          8. int main()
          9. {
          10.     Test t;
          11.     t.a = 5.0;
          12.     printf("%f\n",t.a);
          13.     printf("%d\n",t.i);
          14.     printf("%c,%c,%c,%c\n",t.c[3],t.c[2],t.c[1],t.c[0]);

          15.     t.i = 65;
          16.     printf("%f\n",t.a);
          17.     printf("%d\n",t.i);
          18.     printf("%c,%c,%c,%c\n",t.c[3],t.c[2],t.c[1],t.c[0]);
          19.     return 0;
          20. }

               根據(jù)上面的分析,可以比較方便的計(jì)算出結(jié)果如下所示:

              當(dāng)然這只是我在X86系統(tǒng)中的輸出,在大端系統(tǒng)中會(huì)是什么結(jié)果我不得而知,具體的要參看IEEE標(biāo)準(zhǔn)。

              float類(lèi)型的數(shù)據(jù)是比較復(fù)雜的問(wèn)題,我將在后面認(rèn)真研究,爭(zhēng)取早日解決各種問(wèn)題。

           

          posted on 2013-11-11 18:42 ** 閱讀(199) 評(píng)論(0)  編輯  收藏


          只有注冊(cè)用戶(hù)登錄后才能發(fā)表評(píng)論。


          網(wǎng)站導(dǎo)航:
           

          導(dǎo)航

          統(tǒng)計(jì)

          公告

          你好!

          常用鏈接

          留言簿(2)

          隨筆檔案

          文章分類(lèi)

          文章檔案

          新聞檔案

          相冊(cè)

          收藏夾

          C#學(xué)習(xí)

          友情鏈接

          搜索

          最新評(píng)論

          閱讀排行榜

          評(píng)論排行榜

          主站蜘蛛池模板: 准格尔旗| 汝城县| 宣威市| 灯塔市| 永清县| 武平县| 卫辉市| 曲麻莱县| 衡南县| 保定市| 阳东县| 邓州市| 通道| 汉沽区| 平谷区| 子长县| 望谟县| 屯昌县| 横峰县| 扬州市| 曲沃县| 兰州市| 清远市| 司法| 疏勒县| 自治县| 开原市| 钟山县| 星座| 泗阳县| 潮州市| 三原县| 阳泉市| 平和县| 石阡县| 阿克陶县| 柘荣县| 青阳县| 黔江区| 河池市| 巩义市|