c語言浮點數內存存儲解析和浮點數(double、float)如何定義NaN、正無窮(inf)、負無窮(-inf),以及如何判斷是否是NaN

C語言浮點數存儲方式

一. 浮點數內存存儲方式

對於浮點類型的數據採用單精度類型(float)和雙精度類型(double)來存儲,float數據佔用 32bit,double數據佔用 64bit.其實不論是float類型還是double類型,在計算機內存中的存儲方式都是遵從IEEE的規範的,float 遵從的是IEEE R32.24 ,而double 遵從的是R64.53。   

無論是單精度還是雙精度,在內存存儲中都分爲3個部分:  

1) 符號位(Sign):0代表正,1代表爲負;  

2) 指數位(Exponent):用於存儲科學計數法中的指數數據,並且採用移位存儲; 

3) 尾數部分(Mantissa):尾數部分

其中float的存儲方式如下圖所示:


而雙精度的存儲方式爲:

R32.24和R64.53的存儲方式都是用科學計數法來存儲數據的

用二進制的科學計數法第一位都是1嘛,幹嘛還要表示呀?可以將小數點前面的1省略,所以23bit的尾數部分,可以表示的精度卻變成了 24bit,道理就是在這裏。

那24bit能精確到小數點後幾位呢,我們知道9的二進制表示爲1001,所以4bit能精確十進制中的1位小數 點,24bit就能使float能精確到小數點後6位,而對於指數部分,因爲指數可正可負,8位的指數位能表示的指數範圍就應該爲:-127-128了, 所以指數部分的存儲採用移位存儲,存儲的數據爲元數據+127。

 

  二. 十進制小數轉換爲二進制小數


 十進制小數轉換成二進制小數採用"乘2取整,順序排列"法。具體做法是:用2乘十進制小數,可以得到積,將積的整數部分取出,再用2乘餘下的小數部分,又得到一個積,再將積的整數部分取出,如此進行,直到積中的小數部分爲零,或者達到所要求的精度爲止。 然後把取出的整數部分按順序排列起來,先取的整數作爲二進制小數的高位有效位,後取的整數作爲低位有效位。

    【例1】把(0.8125)轉換爲二進制小數。
    解: 

   【例2】(173.8125)10=( )2

解:(173)10=(10101101)2
由[例1]得(0.8125)10=(0.1101)2
把整數部分和小數部分合並得: (173.8125)10=(10101101.1101)2

下面就看看8.25和120.5在內存中真正的存儲方式:

首先看下8.25,用二進制的科學計數法表示爲 ,首先(8.25)10 = (1000.01)2=(1.00001*2^3)
=1.00001*2^3  按照上面的存儲方式,符號位爲0,表示爲正;指數位爲3+127=130,位數部分爲 1.00001,故8.25的存儲方式如下:  0x41040000 = 0100  0001 0000 0100 0000 0000 0000 0000 

分解如下:0--10000010--00001000000000000000000  

符號位爲0,指數部分爲10000010,位數部分爲 00001000000000000000000

 

同理,120.5在內存中的存儲格式如下: (120.5)10 = (01111000.1)2=(1.1110001*2^6)

=1.1110001*2^6  按照上面的存儲方式,符號位爲0,表示爲正;指數位爲6+127=133,位數部分爲 1.1110001,故120.5的存儲方式如下:  0x41040000 = 0100  0010 1111 0001 0000 0000 0000 0000 

 0x42f10000:    0100  0010 1111 0001 0000 0000 0000 0000  

分解如下:0--10000101--11100010000000000000000  

 

  三. 二進制小數轉換爲十進制小數

    由二進制小數轉換成十進制小數的基本做法是,把二進制數首先寫成加權係數展開式,然後按十進制加法規則求和。這種做法稱爲"按權相加"法。
    例1 把二進制小數110.11轉換成十進制小數。

那麼如果給出內存中一段數據,並且告訴你是單精度存儲的話,你如何知道該數據的十進制數值呢?其實就是對上面的反推過程,比如給出如下內存數據: 01000001001000100000000000000000 

第一步:符號位爲0,表示是正數;  

第二步:指數位爲10000010,換算成十進制爲130,所以指數爲130-127=3;

第三步:尾數位爲01000100000000000000000,

第四步:二進制表示爲:1.01000100000000000000000因爲指數爲+3(右移3),-3(左移位3)所以也等價於

1010.00100000000000000000 = 10.00100000000000000000=10+(0/2+0/4+1/8)=10.125

【同例1】:換算成十進制爲 (1+1/4+1/64); 所以相應的十進制數值爲:2^3*(1+1/4+1/64)=8+2+1/8=10.125

 

浮點數(double、float)如何定義NaN、正無窮、負無窮,以及如何判斷是否是NaN


一.  原理

NaN :指數位的每個二進制位全爲1  並且尾數不爲0;

無窮 :指數位的每個二進制位全爲1並且尾數爲0;符號位爲0,是正無窮,符號位爲1是負無窮。

所以NaN、正無窮、負無窮可以如此定義,可以如此判斷是否NaN:

二.  判斷方法

static inline bool isInf(double d)
{
    uchar *ch = (uchar *)&d;
    if (QSysInfo::ByteOrder == QSysInfo::BigEndian) {
        return (ch[0] & 0x7f) == 0x7f && ch[1] == 0xf0;
    } else {
        return (ch[7] & 0x7f) == 0x7f && ch[6] == 0xf0;
    }

}

static inline bool isNan(double d)
{
    uchar *ch = (uchar *)&d;
    if (QSysInfo::ByteOrder == QSysInfo::BigEndian) {
        return (ch[0] & 0x7f) == 0x7f && ch[1] > 0xf0;
    } else {
        return (ch[7] & 0x7f) == 0x7f && ch[6] > 0xf0;
    }
}

static inline bool isFinite(double d)
{
    uchar *ch = (uchar *)&d;
    if (QSysInfo::ByteOrder == QSysInfo::BigEndian) {
        return (ch[0] & 0x7f) != 0x7f || (ch[1] & 0xf0) != 0xf0;
    } else {
        return (ch[7] & 0x7f) != 0x7f || (ch[6] & 0xf0) != 0xf0;
    }
}

static inline bool isInf(float d)
{
    uchar *ch = (uchar *)&d;
    if (QSysInfo::ByteOrder == QSysInfo::BigEndian) {
        return (ch[0] & 0x7f) == 0x7f && ch[1] == 0x80;
    } else {
        return (ch[3] & 0x7f) == 0x7f && ch[2] == 0x80;
    }
}

static inline bool isNan(float d)
{
    uchar *ch = (uchar *)&d;
    if (QSysInfo::ByteOrder == QSysInfo::BigEndian) {
        return (ch[0] & 0x7f) == 0x7f && ch[1] > 0x80;
    } else {
        return (ch[3] & 0x7f) == 0x7f && ch[2] > 0x80;
    }
}

static inline bool isFinite(float d)
{
    uchar *ch = (uchar *)&d;
    if (QSysInfo::ByteOrder == QSysInfo::BigEndian) {
        return (ch[0] & 0x7f) != 0x7f || (ch[1] & 0x80) != 0x80;
    } else {
        return (ch[3] & 0x7f) != 0x7f || (ch[2] & 0x80) != 0x80;
    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章