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;
}
}