IEEE754標(biāo)準(zhǔn): 一 , 浮點(diǎn)數(shù)在內(nèi)存中的存儲(chǔ)方式

一. 什么是IEEE754標(biāo)準(zhǔn)

我們知道, 計(jì)算機(jī)內(nèi)部實(shí)際上只能存儲(chǔ)或識(shí)別二進(jìn)制.

在計(jì)算機(jī)中, 我們?nèi)粘K褂玫奈臋n, 圖片, 數(shù)字等, 在儲(chǔ)存時(shí), 實(shí)際上都要以二進(jìn)制的形式存放在內(nèi)存或硬盤中, 內(nèi)存或硬盤就好像是一個(gè)被劃分為許多小格子的容器, 其中每個(gè)小格子都只能盛放0或1...

我們?nèi)粘J褂?浮點(diǎn)數(shù) 也不例外, 最終也要被存儲(chǔ)到這樣的二進(jìn)制小格子中.

這就涉及到了 應(yīng)該怎么存 的問(wèn)題, 比如, 對(duì)于浮點(diǎn)數(shù) 20.5, 是應(yīng)該存儲(chǔ)為 0100011 呢, 還是應(yīng)該存儲(chǔ)為 1100110 呢?

事實(shí)上直到20世紀(jì)80年代, 還是計(jì)算機(jī)廠商各自為戰(zhàn), 每家都在設(shè)計(jì)自己的浮點(diǎn)數(shù)存儲(chǔ)規(guī)則, 彼此之間并不兼容. 直到1985年, IEEE754標(biāo)準(zhǔn)問(wèn)世, 浮點(diǎn)數(shù)的存儲(chǔ)問(wèn)題才有了一個(gè)通用的工業(yè)標(biāo)準(zhǔn).

IEEE754標(biāo)準(zhǔn)提供了如何在計(jì)算機(jī)內(nèi)存中,以二進(jìn)制的方式存儲(chǔ)十進(jìn)制浮點(diǎn)數(shù)的具體標(biāo)準(zhǔn),

IEEE754標(biāo)準(zhǔn)發(fā)布于1985年. 包括 javascript, Java, C在內(nèi)的許多編程語(yǔ)言在實(shí)現(xiàn)浮點(diǎn)數(shù)時(shí), 都遵循IEEE754標(biāo)準(zhǔn).

IEEE754的最新標(biāo)準(zhǔn)是IEEE754-2008, 但本篇文章主要參考的是IEEE754-1985, 好在兩者相差并不大, 而參照1985的標(biāo)準(zhǔn)可以讓我們對(duì)一些基礎(chǔ)概念有更好的理解

IEEE754提供了四種精度規(guī)范, 其中最常用的是 單精度浮點(diǎn)型 雙精度浮點(diǎn)型 , 但I(xiàn)EEE754并沒(méi)有規(guī)定32位浮點(diǎn)數(shù)類型需要叫做 float, 或64位浮點(diǎn)數(shù)需要叫做 double. 它只是提供了一些關(guān)于如何存儲(chǔ)不同精度浮點(diǎn)數(shù)的規(guī)范和標(biāo)準(zhǔn). 不過(guò)一般情況下, 如果我們提到 float, 其實(shí)指的就是IEEE754標(biāo)準(zhǔn)中的32位單精度浮點(diǎn)數(shù). 如果我們提到 double, 其實(shí)指的就是IEEE754標(biāo)準(zhǔn)中的64位雙精度浮點(diǎn)數(shù)

下面是單精度浮點(diǎn)數(shù)和雙精度浮點(diǎn)數(shù)的一些信息, 可以先簡(jiǎn)單看一下, 看不懂也沒(méi)關(guān)系, 下文會(huì)對(duì)這里的信息做詳細(xì)的解釋...

單雙精度浮點(diǎn)數(shù)對(duì)比

好啦, 鋪墊完了, 開始正文吧~


二. 32位單精度浮點(diǎn)數(shù)在內(nèi)存中的存儲(chǔ)方式

上文說(shuō)到: IEEE754標(biāo)準(zhǔn)提供了如何在計(jì)算機(jī)內(nèi)存中, 以二進(jìn)制的方式存儲(chǔ)十進(jìn)制浮點(diǎn)數(shù)的具體標(biāo)準(zhǔn), 并制定了四種精度規(guī)范.

這里我們主要研究 32位浮點(diǎn)數(shù) (或者說(shuō)單精度浮點(diǎn)數(shù), 或者說(shuō)float類型) 在計(jì)算機(jī)中是怎么存儲(chǔ)的. 其他精度, 比如64位浮點(diǎn)數(shù), 則大同小異.

想要存儲(chǔ)一個(gè)32位浮點(diǎn)數(shù), 比如20.5, 在內(nèi)存或硬盤中要占用32個(gè)二進(jìn)制位 (或者說(shuō)32個(gè)小格子, 32個(gè)比特位)

這32個(gè)二進(jìn)制位被劃分為3部分, 用途各不相同:

32位浮點(diǎn)數(shù)內(nèi)存占用示意圖, 共使用了32個(gè)小格子

這32個(gè)二進(jìn)制位的內(nèi)存編號(hào)從高到低 (從31到0), 共包含如下幾個(gè)部分:

sign: 符號(hào)位, 即圖中藍(lán)色的方塊

biased exponent: 偏移后的指數(shù)位, 即圖中綠色的方塊

fraction: 尾數(shù)位, 即圖中紅色的方塊

下面會(huì)依次介紹這三個(gè)部分的概念, 用途.


1. 符號(hào)位: sign

以32位單精度浮點(diǎn)數(shù)為例, 以下不再贅述:

符號(hào)位: 占據(jù)最高位(第31位)這一位, 用于表示這個(gè)浮點(diǎn)數(shù)是正數(shù)還是負(fù)數(shù), 為0表示正數(shù), 為1表示負(fù)數(shù).

舉例: 對(duì)于十進(jìn)制數(shù)20.5, 存儲(chǔ)在內(nèi)存中時(shí), 符號(hào)位為0, 因?yàn)檫@是個(gè)正數(shù)


2. 偏移后的指數(shù)位: biased exponent

指數(shù)位占據(jù)第30位到第23位這8位. 用于表示以2位底的指數(shù). 至于這個(gè)指數(shù)的作用, 后文會(huì)詳細(xì)講解, 這里只需要知道: 8位二進(jìn)制可以表示256種狀態(tài), IEEE754規(guī)定, 指數(shù)位用于表示[-127, 128]范圍內(nèi)的指數(shù).

不過(guò)為了表示起來(lái)更方便, 浮點(diǎn)型的指數(shù)位都有一個(gè)固定的偏移量(bias), 用于使 指數(shù) + 這個(gè)偏移量 = 一個(gè)非負(fù)整數(shù). 這樣指數(shù)位部分就不用為如何表示負(fù)數(shù)而擔(dān)心了. 規(guī)定: 在32位單精度類型中, 這個(gè)偏移量是127. 在64位雙精度類型中, 偏移量是1023.

所以這里的偏移量是127,

即, 如果你運(yùn)算后得到的指數(shù)是 -127, 那么偏移后, 在指數(shù)位中需要表示為: -127 + 127(偏移量) = 0

如果你運(yùn)算后得到的指數(shù)是 -10, 那么偏移后, 在指數(shù)位中需要表示為: -10 + 127(偏移量) = 117

看, 有了偏移量, 指數(shù)位中始終是一個(gè)非負(fù)整數(shù).

看到這里, 可能會(huì)覺(jué)得還不是很清楚指數(shù)的作用到的是什么. 沒(méi)關(guān)系, 讓我們先繼續(xù)往下看吧...


3. 尾數(shù)位:fraction

尾數(shù)位: 占據(jù)剩余的22位到0位這23位. 用于存儲(chǔ)尾數(shù).

在以二進(jìn)制格式存儲(chǔ)十進(jìn)制浮點(diǎn)數(shù)時(shí), 首先需要把十進(jìn)制浮點(diǎn)數(shù)表示為二進(jìn)制格式, 還拿十進(jìn)制數(shù)20.5舉例:

十進(jìn)制浮點(diǎn)數(shù)20.5 = 二進(jìn)制10100.1

然后需要把這個(gè)二進(jìn)制數(shù)轉(zhuǎn)換為以2為底的指數(shù)形式:

二進(jìn)制10100.1 = 1.01001 * 2^4

注意轉(zhuǎn)換時(shí), 對(duì)于乘號(hào)左邊, 加粗的那個(gè)二進(jìn)制數(shù)1.01001, 需要把小數(shù)點(diǎn)放在左起第一位和第二位之間. 且第一位需要是個(gè)非0數(shù). 這樣表示好之后, 其中的1.01001就是尾數(shù).

用 二進(jìn)制數(shù) 表示 十進(jìn)制浮點(diǎn)數(shù) 時(shí), 表示為尾數(shù)*指數(shù)的形式, 并把尾數(shù)的小數(shù)點(diǎn)放在第一位和第二位之間, 然后保證第一位數(shù)非0, 這個(gè)處理過(guò)程叫做規(guī)范化(normalized)

我們?cè)賮?lái)看看規(guī)范化之后的這個(gè)數(shù): 1.01001 * 2^4

其中1.01001是尾數(shù),? 而4就是偏移前的指數(shù)(unbiased exponent), 上文講過(guò), 32位單精度浮點(diǎn)數(shù)的偏移量(bias)為127, 所以這里加上偏移量之后, 得到的偏移后指數(shù)(biased exponent)就是 4 + 127 = 131, 131轉(zhuǎn)換為二進(jìn)制就是1000 0011

現(xiàn)在還需要對(duì)尾數(shù)做一些特殊處理

1. 隱藏高位1.

你會(huì)發(fā)現(xiàn), 尾數(shù)部分的最高位始終為1. 比如這里的 1.01001, 這是因?yàn)榍懊嬲f(shuō)過(guò), 規(guī)范化之后, 尾數(shù)中的小數(shù)點(diǎn)會(huì)位于左起第一位和第二位之間. 且第一位是個(gè)非0數(shù). 而二進(jìn)制中, 每一位可取值只有0或1, 如果第一位非0, 則第一位只能為1. 所以在存儲(chǔ)尾數(shù)時(shí), 可以省略前面的 1和小數(shù)點(diǎn). 只記錄尾數(shù)中小數(shù)點(diǎn)之后的部分, 這樣就節(jié)約了一位內(nèi)存. 所以這里只需記錄剩余的尾數(shù)部分: 01001

所以, 以后再提到尾數(shù), 如無(wú)特殊說(shuō)明, 指的其實(shí)是隱藏了整數(shù)部分1. 之后, 剩下的小數(shù)部分

2. 低位補(bǔ)0

有時(shí)候尾數(shù)會(huì)不夠填滿尾數(shù)位(即圖中的紅色格子). 比如這里的, 尾數(shù)01001不夠23位

此時(shí), 需要在低位補(bǔ)零, 補(bǔ)齊23位.

之所以在低位補(bǔ)0, 是因?yàn)槲矓?shù)中存儲(chǔ)的本質(zhì)上是二進(jìn)制的小數(shù)部分, 所以如果想要在不影響原數(shù)值的情況下, 填滿23位, 就需要在低位補(bǔ)零.

比如,? 要把二進(jìn)制數(shù)1.01在不改變?cè)档那闆r下填滿八位內(nèi)存, 寫出來(lái)就應(yīng)該是: 1.010 0000, 即需要在低位補(bǔ)0

同理, 本例中因?yàn)槲矓?shù)部分存儲(chǔ)的實(shí)際上是省略了整數(shù)部分 1. 之后, 剩余的小數(shù)部分, 所以這里補(bǔ)0時(shí)也需要在低位補(bǔ)0:

原尾數(shù)是:? ? 01001(不到23位)

補(bǔ)零之后是:? 0100 1000 0000 0000 000? (補(bǔ)至23位)


三. 實(shí)例: 表示十進(jìn)制浮點(diǎn)數(shù)20.5

在上面的討論中, 我們已經(jīng)得出, 十進(jìn)制浮點(diǎn)數(shù) 20.5 的:

符號(hào)位是: 0

偏移后指數(shù)位是: 1000 0011

補(bǔ)零后尾數(shù)位是: 0100 1000 0000 0000 000

現(xiàn)在, 把這三部分按順序放在32位浮點(diǎn)數(shù)容器中, 就是 0? ? 1000 0011? ? 0100 1000 0000 0000 000

這就在32位浮點(diǎn)數(shù)容器中, 以二進(jìn)制表示了一個(gè)十進(jìn)制數(shù)20.5的方式

這里有一個(gè)可以驗(yàn)證的IEEE754浮點(diǎn)數(shù)內(nèi)存狀態(tài)的網(wǎng)站, 我們來(lái)驗(yàn)證一下:

可見驗(yàn)證是通過(guò)的. 不過(guò)為了加深理解, 我們?cè)俜聪蛲茖?dǎo)一遍:


假設(shè)現(xiàn)在我們有一個(gè)用二進(jìn)制表示的32位浮點(diǎn)數(shù): 0? 1000 0011? 0100 1000 0000 0000 000, 求它所代表的十進(jìn)制浮點(diǎn)數(shù)是多少?

觀察可知:

符號(hào)位是0: 所以這是個(gè)正數(shù).

尾數(shù)是: 0100 1000 0000 0000 000

去掉后面的補(bǔ)零, 再加上隱藏的整數(shù)部分1.? 得到完整的尾數(shù)(含隱藏的整數(shù)部分)為: 1.01001

偏移后的指數(shù)位為: 1000 0011, 轉(zhuǎn)換為十進(jìn)制為131, 減去偏移量127, 得到真正的指數(shù)是 4

所以, 最后得到的浮點(diǎn)數(shù) = 尾數(shù)(含隱藏的整數(shù)部分) * 以2為底的指數(shù)次冪

=? 二進(jìn)制的: 1.01001 * 2^4

=? 把小數(shù)點(diǎn)向右移動(dòng)4位

=? 二進(jìn)制的10100.1

=? 十進(jìn)制位20.5

注意, 直到最后一步才把二進(jìn)制轉(zhuǎn)換為十進(jìn)制.

附帶的, 這里還有一個(gè)進(jìn)制轉(zhuǎn)換網(wǎng)站, 可以看到二進(jìn)制的10100.1, 確實(shí)等于十進(jìn)制的20.5

到這里就講解的差不多了,

隨后是一張大體的計(jì)算方法示意圖

還有雙精度類型的內(nèi)存狀態(tài)示意圖:

下一篇會(huì)講述為什么32位單精度浮點(diǎn)數(shù)的取值范圍是±1.18*10^{-38} 到? ±3.4 * 10^{38}, 這個(gè)值究竟是如何計(jì)算出來(lái)的...

下一篇再見吧~


注:

這一系列文章其實(shí)是我在學(xué)習(xí)IEEE754標(biāo)準(zhǔn)的過(guò)程中, 總結(jié)的一系列筆記. 其中包含了一些個(gè)人理解, 所以如有偏差, 還望指出.

整個(gè)系列大概會(huì)包含如下五篇文章:

一. 浮點(diǎn)數(shù)在內(nèi)存中的存儲(chǔ)方式

二. 浮點(diǎn)數(shù)的取值范圍是如何計(jì)算的

三. 浮點(diǎn)數(shù)的精度是如何計(jì)算的

四. 非規(guī)格數(shù), ±0, ±infinity和NaN都是什么

五. 浮點(diǎn)數(shù)的舍入規(guī)則(rounding)

下一篇再見~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請(qǐng)通過(guò)簡(jiǎn)信或評(píng)論聯(lián)系作者。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容