IEEE754標準: 一 , 浮點數(shù)在內(nèi)存中的存儲方式

一. 什么是IEEE754標準

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

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

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

這就涉及到了 應該怎么存 的問題, 比如, 對于浮點數(shù) 20.5, 是應該存儲為 0100011 呢, 還是應該存儲為 1100110 呢?

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

IEEE754標準提供了如何在計算機內(nèi)存中,以二進制的方式存儲十進制浮點數(shù)的具體標準,

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

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

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

下面是單精度浮點數(shù)和雙精度浮點數(shù)的一些信息, 可以先簡單看一下, 看不懂也沒關系, 下文會對這里的信息做詳細的解釋...

單雙精度浮點數(shù)對比

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


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

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

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

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

這32個二進制位被劃分為3部分, 用途各不相同:

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

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

sign: 符號位, 即圖中藍色的方塊

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

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

下面會依次介紹這三個部分的概念, 用途.


1. 符號位: sign

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

符號位: 占據(jù)最高位(第31位)這一位, 用于表示這個浮點數(shù)是正數(shù)還是負數(shù), 為0表示正數(shù), 為1表示負數(shù).

舉例: 對于十進制數(shù)20.5, 存儲在內(nèi)存中時, 符號位為0, 因為這是個正數(shù)


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

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

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

所以這里的偏移量是127,

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

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

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

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


3. 尾數(shù)位:fraction

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

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

十進制浮點數(shù)20.5 = 二進制10100.1

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

二進制10100.1 = 1.01001 * 2^4

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

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

我們再來看看規(guī)范化之后的這個數(shù): 1.01001 * 2^4

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

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

1. 隱藏高位1.

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

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

2. 低位補0

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

此時, 需要在低位補零, 補齊23位.

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

比如,? 要把二進制數(shù)1.01在不改變原值的情況下填滿八位內(nèi)存, 寫出來就應該是: 1.010 0000, 即需要在低位補0

同理, 本例中因為尾數(shù)部分存儲的實際上是省略了整數(shù)部分 1. 之后, 剩余的小數(shù)部分, 所以這里補0時也需要在低位補0:

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

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


三. 實例: 表示十進制浮點數(shù)20.5

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

符號位是: 0

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

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

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

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

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

可見驗證是通過的. 不過為了加深理解, 我們再反向推導一遍:


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

觀察可知:

符號位是0: 所以這是個正數(shù).

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

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

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

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

=? 二進制的: 1.01001 * 2^4

=? 把小數(shù)點向右移動4位

=? 二進制的10100.1

=? 十進制位20.5

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

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

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

隨后是一張大體的計算方法示意圖

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

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

下一篇再見吧~


注:

這一系列文章其實是我在學習IEEE754標準的過程中, 總結的一系列筆記. 其中包含了一些個人理解, 所以如有偏差, 還望指出.

整個系列大概會包含如下五篇文章:

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

二. 浮點數(shù)的取值范圍是如何計算的

三. 浮點數(shù)的精度是如何計算的

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

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

下一篇再見~

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

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

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