理解C語言浮點(diǎn)數(shù)的存儲

IEEE-754標(biāo)準(zhǔn)

目前世界上使用最為廣泛的小數(shù)表示方法是浮點(diǎn)數(shù)表示法,而浮點(diǎn)數(shù)通用的算術(shù)標(biāo)準(zhǔn)是IEEE-754標(biāo)準(zhǔn)溢十。什么是IEEE?

IEEE 電氣和電子工程師協(xié)會(IEEE,全稱是Institute of Electrical and Electronics Engineers)是一個美國的電子技術(shù)與信息科學(xué)工程師的協(xié)會拔鹰,是世界上最大的非營利性專業(yè)技術(shù)學(xué)會,其會員人數(shù)超過40萬人涣觉,遍布160多個國家痴荐。IEEE致力于電氣、電子官册、計(jì)算機(jī)工程和與科學(xué)有關(guān)的領(lǐng)域的開發(fā)和研究生兆,在航空航天、信息技術(shù)膝宁、電力及消費(fèi)性電子產(chǎn)品等領(lǐng)域鸦难,已制定了900多個行業(yè)標(biāo)準(zhǔn),現(xiàn)已發(fā)展成為具有較大影響力的國際學(xué)術(shù)組織

—— 引自百度百科

之所以要寫這么一篇文章员淫,是因?yàn)槲蚁胍愣?C語言對doublefloat的表示和存儲細(xì)節(jié)合蔽。之前自己弄懂了,不過由于沒有對文件進(jìn)行備份满粗,導(dǎo)致我的實(shí)驗(yàn)的代碼和筆記都被誤刪了辈末,連帶被誤刪的還有一篇探究字節(jié)序(大小端)的筆記和代碼,以及一篇關(guān)于開平方根算法和編碼的筆記(X86架構(gòu)有開平方根的及其指令映皆,超快)挤聘。這些使我意識到了將筆記轉(zhuǎn)化為文章并分享到網(wǎng)上的必要性。

這篇文章并非細(xì)致認(rèn)真的標(biāo)準(zhǔn)解讀手冊捅彻,只是想探究一下doublefloat的二進(jìn)制存儲序列组去。

C 中的浮點(diǎn)數(shù)表示

我們知道,C語言的浮點(diǎn)數(shù)分為單精度和雙精度步淹,單精度的float采用32位二進(jìn)制(4字節(jié))來存儲从隆,而雙精度的double使用64位,另外還有一種占80位(10個字節(jié))的臨時數(shù)缭裆。

一個浮點(diǎn)數(shù)的存儲分為3個部分键闺,分別是符號位階碼尾數(shù)澈驼,那么這三部分是如何組合而成為一個浮點(diǎn)數(shù)整體的呢辛燥?

對于一個64位浮點(diǎn)數(shù),我們可以用下面的這張示意圖來表示它的各個部分的長度及順序缝其。其中一個等號=表示一個二進(jìn)制位挎塌,|表示隱含的邊界。

|=|===========|===================================================|
|s|-exponent--|--------------------mantissa-----------------------|

上面的圖示中内边,s(sign)為符號位榴都,占 1 bit,用來表示整個double的正負(fù)性漠其;中間部分exponent是指數(shù)部分嘴高,即階碼竿音,占 11 bit;最后的也是最長的一部分mantissa拴驮,尾數(shù)谍失,占52位,它的長度直接影響力浮點(diǎn)數(shù)的精度莹汤。

下面是這三個部分的具體細(xì)節(jié)

  • 符號位跟整數(shù)一樣,符號位取0表示無符號颠印,為正數(shù)纲岭;取1表示有符號,為負(fù)數(shù)线罕。

  • 指數(shù)部分采用的是移碼表示法并且采用的是余1023碼止潮,也就是說,指數(shù)部分的11位沒有符號位钞楼,在計(jì)算時喇闸,要先將這11位看作一個正整數(shù),然后減掉1023之后才得真正的指數(shù)询件。

  • 關(guān)于尾數(shù)部分燃乍,有一點(diǎn)需特別注意,尾數(shù)部分的值總是 1.M宛琅,而1.M中的1是被隱藏了的。為什么呢?這樣做有什么意義眷篇?
    我們知道池摧,一個十進(jìn)制數(shù)采用科學(xué)計(jì)數(shù)法表示的話,形式是 d \times 10^e 其中d \in [1.0, 10)红伦。類推一下英古,就可以知道,一個k進(jìn)制的科學(xué)計(jì)數(shù)法昙读,小數(shù)的整數(shù)部分的取值范圍是[1.0, k)召调,所以在二進(jìn)制中,小數(shù)部分的取值范圍是[1.0, 2)箕戳,這個范圍內(nèi)的實(shí)數(shù)某残,整數(shù)部分都是1,所以這個1是大家共有的陵吸,于是就沒有存儲的必要性了玻墅,因?yàn)?/p>

你愛,或者不愛
愛就在那里壮虫,不增不減

你存澳厢,或者不存
它就在那里环础,不大不小

上面講述了double類型的存儲。一個double64各二進(jìn)制位剩拢,而一個float則占用32位线得,包括1位符號位、8位指數(shù)位和23位尾數(shù)位徐伐。

提取一個double的各個部分

下面贯钩,我們用C語言編寫一個程序來打印一下一個double的各個部分的二進(jìn)制及十進(jìn)制。相信理解了這段代碼办素,你就真的理解浮點(diǎn)數(shù)的表示了角雷。

#include <stdio.h>
#include <assert.h>

#define NM (1LL << 63)  /* negative most */
#define PM ~NM          /* positive most */
#define LL(d) *((long long*)&(d))
#define EZ(d) LL(d) &= (PM >> 1), LL(d) |= (1023LL << 52)

int sign(double d) { return (LL(d) >> 63) & 1LL; }
int exponent(double d) { return (LL(d) >> 52) & 0x7ff; }
// `EZ(d) -> LL(d) &= (PM >> 1), (LL(d) >> 52) & 0x7ff;`
// 將`d`的指數(shù)部分的$11$位填上$1023$(低$10$位全$1$,最高位為$0$)
// 因?yàn)椴捎玫氖怯?1023$碼性穿,所以這條語句的目的是將指數(shù)部分變?yōu)?0$
double mantissa(double d) { return EZ(d), d; }

#define sign(d) (sign(d)? -1 : 1)
#define exponent(d) (exponent(d) - 1023)

/* print binary of a double */
void printbd(double d)
{
    /* from left to right */
    printf("%+g =\n", d);
    printf("%4c", 32);
    for (int i = 0; i < 64; ++i) {
        if (i == 0 || i == 1 || i == 12)
            putchar('|');
        // 這里只能用右移勺三,因?yàn)?(long long -> int) 要截?cái)嗟降?2位
        putchar(((LL(d) >> (63 - i)) & 1LL) + '0');
    }
    printf("|\n");
    printf("%4c", 32);
    printf("%+d * %g * 2^(%d)\n", sign(d), mantissa(d), exponent(d));
}


int main()
{
    assert(sign(+0.5) == +1);
    assert(sign(-0.5) == -1);

    printbd(-0.05);
    printbd(+0.05);
    printbd(-0.5);
    printbd(+0.5);
    printbd(-1.0);
    printbd(+1.0);
    printbd(-2.0);
    printbd(+2.0);
    printbd(-9.0);
    printbd(+9.0);
    printbd(-10.0);
    printbd(+10.0);

    return 0;
}

另外一些需要注意的細(xì)節(jié)

因?yàn)椴捎玫氖且拼a表示法,所以不像補(bǔ)碼表示法需曾,可以直接從二進(jìn)制判斷一個數(shù)的大小吗坚。指數(shù)部分全為1時,指數(shù)部分的取值最大呆万。
對于正負(fù)無窮及不合法的運(yùn)算結(jié)果商源,IEEE標(biāo)準(zhǔn)規(guī)定

  • 如果指數(shù)部分是0并且尾數(shù)的小數(shù)部分是0,則表示±0(符號位相關(guān))桑嘶;
  • 如果指數(shù)部分全為1炊汹,并且尾數(shù)的小數(shù)部分是0,則表示±\infty逃顶;
  • 如果指數(shù)部分全為1讨便,并且尾數(shù)的小數(shù)部分不為0,那么表示Not a Number以政,即NaN霸褒。

附錄

前文代碼的運(yùn)行結(jié)果

-0.05 =
    |1|01111111010|1001100110011001100110011001100110011001100110011010|
    -1 * 1.6 * 2^(-5)
+0.05 =
    |0|01111111010|1001100110011001100110011001100110011001100110011010|
    +1 * 1.6 * 2^(-5)
-0.5 =
    |1|01111111110|0000000000000000000000000000000000000000000000000000|
    -1 * 1 * 2^(-1)
+0.5 =
    |0|01111111110|0000000000000000000000000000000000000000000000000000|
    +1 * 1 * 2^(-1)
-1 =
    |1|01111111111|0000000000000000000000000000000000000000000000000000|
    -1 * 1 * 2^(0)
+1 =
    |0|01111111111|0000000000000000000000000000000000000000000000000000|
    +1 * 1 * 2^(0)
-2 =
    |1|10000000000|0000000000000000000000000000000000000000000000000000|
    -1 * 1 * 2^(1)
+2 =
    |0|10000000000|0000000000000000000000000000000000000000000000000000|
    +1 * 1 * 2^(1)
-9 =
    |1|10000000010|0010000000000000000000000000000000000000000000000000|
    -1 * 1.125 * 2^(3)
+9 =
    |0|10000000010|0010000000000000000000000000000000000000000000000000|
    +1 * 1.125 * 2^(3)
-10 =
    |1|10000000010|0100000000000000000000000000000000000000000000000000|
    -1 * 1.25 * 2^(3)
+10 =
    |0|10000000010|0100000000000000000000000000000000000000000000000000|
    +1 * 1.25 * 2^(3)
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市盈蛮,隨后出現(xiàn)的幾起案子废菱,更是在濱河造成了極大的恐慌,老刑警劉巖抖誉,帶你破解...
    沈念sama閱讀 218,204評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件殊轴,死亡現(xiàn)場離奇詭異,居然都是意外死亡袒炉,警方通過查閱死者的電腦和手機(jī)旁理,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來我磁,“玉大人孽文,你說我怎么就攤上這事驻襟。” “怎么了芋哭?”我有些...
    開封第一講書人閱讀 164,548評論 0 354
  • 文/不壞的土叔 我叫張陵沉衣,是天一觀的道長。 經(jīng)常有香客問我减牺,道長豌习,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,657評論 1 293
  • 正文 為了忘掉前任拔疚,我火速辦了婚禮斑鸦,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘草雕。我一直安慰自己,他們只是感情好固以,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,689評論 6 392
  • 文/花漫 我一把揭開白布墩虹。 她就那樣靜靜地躺著,像睡著了一般憨琳。 火紅的嫁衣襯著肌膚如雪诫钓。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,554評論 1 305
  • 那天篙螟,我揣著相機(jī)與錄音菌湃,去河邊找鬼。 笑死遍略,一個胖子當(dāng)著我的面吹牛惧所,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播绪杏,決...
    沈念sama閱讀 40,302評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼下愈,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了蕾久?” 一聲冷哼從身側(cè)響起势似,我...
    開封第一講書人閱讀 39,216評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎僧著,沒想到半個月后履因,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,661評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡盹愚,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,851評論 3 336
  • 正文 我和宋清朗相戀三年栅迄,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片杯拐。...
    茶點(diǎn)故事閱讀 39,977評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡霞篡,死狀恐怖世蔗,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情朗兵,我是刑警寧澤污淋,帶...
    沈念sama閱讀 35,697評論 5 347
  • 正文 年R本政府宣布,位于F島的核電站余掖,受9級特大地震影響寸爆,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜盐欺,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,306評論 3 330
  • 文/蒙蒙 一赁豆、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧冗美,春花似錦魔种、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至属韧,卻和暖如春安拟,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背宵喂。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評論 1 270
  • 我被黑心中介騙來泰國打工糠赦, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人锅棕。 一個月前我還...
    沈念sama閱讀 48,138評論 3 370
  • 正文 我出身青樓拙泽,卻偏偏與公主長得像,于是被迫代替她去往敵國和親裸燎。 傳聞我的和親對象是個殘疾皇子奔滑,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,927評論 2 355

推薦閱讀更多精彩內(nèi)容