小數在內存中是如何存儲的?

小數在內存中是如何存儲的?

文本關鍵字:小數、float、double、浮點數、精度java

1、IEEE 754(二進制浮點數算術標準)

在學習進制轉換時,咱們瞭解到:咱們常用的十進制數是轉換爲二進制進行存儲的,只須要按照順序將轉換後的結果放在對應的位置上就好了。其實小數的存儲也是基於二進制的,不過因爲小數由整數部分和小數部分組成,爲了方便表示和比較,會使用另外的方式來存儲。
IEEE 754是最普遍使用的浮點數運算標準,在標準中規定了四種表示浮點數值的方式:ide

  • 單精度:32位 - 4字節
  • 雙精度:64位 - 8字節
  • 延伸單精度:43+
  • 延伸雙精度:79+
    對於進制轉換不清楚的同窗能夠進傳送門:進制之間如何轉換?

    1. 存儲結構

    小數在內存中的存儲由三部分組成,分別是符號、階碼(或稱指數)、尾數。符號位咱們很熟悉,只佔一位,而且出如今最高位,0爲正,1爲負。學習

  • 單精度:符號1位,階碼8位,尾數23位
  • 雙精度:符號1位,階碼11位,尾數52位
  • 延伸精度不多使用,不作介紹

小數在內存中是如何存儲的?

2. 存儲方式

一個十進制的小數在進行存儲時,首先要將整數部分與小數部分都轉換爲二進制,而後再整理成相似科學技術法的形式,即:移動小數點,使得小數點的左邊只有一位,而且只可能爲1(由於是二進制),小數點右側的部分即爲尾數部分,移動小數點的位數將會被記錄在指數部分中。爲了可以透徹的理解十進制小數轉化存儲在內容中的過程,咱們還須要瞭解一個概念:階碼。code

2、階碼(指數)

1. 定義

對於一個二進制數,咱們總能夠把它整理成:尾數 ✖️ 2的P次方的形式,其中P就被定義爲階碼,咱們也能夠認爲2是底數,P爲指數,以整數形式表示。blog

2. 爲何小數被稱做浮點數?

  • 定點小數

在早期計算機中,爲了節省硬件資源,階碼P的值是被固定的,那麼小數的表示形式也同時被固定了。規定第一位爲符號位,小數點固定在第一位後面,這種小數是純小數,被稱爲定點小數。內存

  • 浮點小數

與定點小數相對的,若是階碼P可變,那這種小數表示法就被稱爲浮點表示,這樣的數也就被稱爲浮點數了。更爲重要的一點,P指明瞭小數點的位置。ci

3. 移碼

明白了階碼的概念,也瞭解了浮點數的前世此生,那麼咱們大費周章的說這個概念幹什麼呢?沒錯,重點來了,就是爲了這個移碼的碼制。在進行小數點移動時,須要先將十進制數轉換爲二進制,再去移動小數點,保證小數點左側只有一位,且數值爲1。資源

  • 對於絕對值大於2的數,這個時候咱們向左移動小數點,對應的指數爲正數;
  • 對於一個絕對值小於1的數,這個時候咱們向右移動小數點,對應的指數爲負數;
  • 絕對值在1和2之間的數嘞?這個時候不用移動好叭。。。

那麼問題就來了,咱們的指數有的時候正,有的時候負。But!更爲嚴重的問題是,在指數部分對應的區間並無符號位這個東西,最前面的符號位表明的是小數自己的正負,這就使得存儲和比較都變得困難,因此咱們但願經過一種修正的方式避開正負號的問題。怎麼作呢?以float爲例,指數部分長度爲8。
原有帶符號位的8個bit的存儲範圍是-128 ~ 127(不明白的同窗能夠進傳送門爲何一個byte的存儲範圍是-128~127?),也就是說能夠記錄-128次方到+127方之間的全部指數值。若是忽略符號位,把它也當作一個數據的存儲位,那麼範圍就是0~255,咱們取這個數的一半做爲修正值,即:127,把每次移動小數點後得到的指數值都加上127。get

  • 小數點向左移動3位,對應的指數爲+3,存入指數部分的值即爲130的二進制表示
  • 小數點向右移動2位,對應的指數爲-2,存入指數部分的值即爲125的二進制表示

這樣的好處就是避開了符號的問題,同時,原有的指數的值也獲得的了保存,取出的時候減掉127就行了。那麼直觀的講,原來的範圍是-128 ~ 127,加上127以後範圍應該變成-1 ~ 254,貌似對應關係有問題呀~這實際上是一個很簡單的二進制換算問題,對於有符號數,最高位爲符號位,用1表明負數,-128的補碼爲:1000 0000,可是這在無符號數眼裏的值爲128,-1的補碼爲:1111 1111,可是在無符號數眼裏值爲255。因此咱們不能直接經過加減法得出這個取值範圍,而應該結合二進制存儲的規則(不明白的同窗能夠二進傳送門查看補碼相關的知識:爲何一個byte的存儲範圍是-128~127?)。it

3、小數的進制轉換

說了這麼久,咱們用幾個例子來給你們演示一下,會給你們列出小數在內存中存儲的完整表示,在這以前仍是須要先學習一下十進制小數應該怎麼轉換爲二進制(讀者心裏:我太難了。。。)。

1. 十進制轉二進制

小數分爲整數部分和小數部分,整數部分的轉換照常進行,不斷的除2獲得,小數部分恰好是不斷的乘2獲得,一直到小數部分爲0,或者已經達到了對應的精度,以69.3125爲例。

  • 整數部分:69 = 64 + 4 + 1 = 2^6 + 2^2 + 2^0
    • 對應的二進制數爲:0100 0101
  • 小數部分:轉換過程以下 -> 不斷乘2,取出結果中的整數部分
    • 對應的二進制數爲:0101

小數在內存中是如何存儲的?

  • 最終轉換結果:0100 0101.0101

    2. 二進制轉十進制

    由二進制轉換爲十進制比較簡單,就是運算規則作相反的運算,整數部分是作除法獲得的,那麼轉換回去的時候就是作乘法,小數部分是作乘法獲得的,那麼轉換回去的時候就作除法,以0100 0101.0101爲例。

  • 整數部分:2^6 + 2^2 + 2^0 = 64 + 4 + 1 = 69
  • 小數部分:0 x 2^-1 + 1 x 2^-2 + 0 x 2^-3 + 1 x 2^-4 = 0.3125

能夠看到規律實際上是統一的,就是從左至右根據二進制數乘以2的n次方,從左至右n的值不斷遞減,在個位處,n的值爲0,進入小數部分n的值爲負數,在運算上的體現爲除法。

3. 小數在內存中的存儲表示

  • 99.9

9.9的二進制表示:1100011.111001100110011001100110011001100110011001101。如今咱們須要將小數點左移6位,對應的指數值爲+6。此時小數點右側的位數爲51位,這些將會被存放在尾數部分,若是使用double類型能夠將數據所有記錄,可是若是使用float類型,因爲尾數部分只有23位,全部只能記錄部分的數據,偏差也就產生了!
整理一下,符號位爲0,指數部分爲6+127=133,尾數部分直接丟進去,能裝多少裝多少,以float爲例。
最終表示爲:0 10000101 10001111100110011001100

  • 0.226

0.226的二進制表示:0.0011100111011011001000101101000011100101011000000100001。此時小數點須要右移3位,對應的指數值爲-3,剩下的尾數部分一樣能塞多少塞多少。
整理一下,符號位爲0,指數部分爲-3+127=124,以float爲例。
最終表示爲:0 1111100 11001110110110010001011

4、float與double

1. 精度範圍

從上面的例子咱們能夠看到,當一個小數在存儲的過程當中,偏差就已經產生了,並且因爲是轉換爲二進制存儲,咱們很難對全部的小數進行判斷是否在存儲時丟失了精度。看下面幾個例子:

public static void main(String[] args) {
        Float f1 = 99.99999f;
        System.out.println(f1);// 輸出結果:99.99999
        // 貌似很正常啊,其實float的心裏慌的一批
        Float f2 = 99.999999f;
        System.out.println(f2);// 輸出結果:100.0
        // 此時終於暴露了吧?在存儲時就已經丟失了精度,在參與小數計算時更加暴露無遺
    }
  • float精度:小數點後6~7位
  • double精度:小數點後15~16位

丟失精度的緣由通過上面的分析和例子相信你們應該很清楚了,咱們按照常規流程進行二進制轉換後獲得的尾數部分可能很長,可是以單精度或雙精度進行存儲時只能存儲一部分,那麼必然致使精度的丟失。

2. 解決精度不足

float和double做爲基本數據類型使用起來固然是比較方便,可是精度的問題會形成不許確,雖然咱們能夠經過使用保留幾位小數的方式勉強應對,可是爲了保證高精度一般會使用BigDecimal,具體用法不在此贅述,將在後續文章中說明。

3. 與長整型的比較

咱們在接觸基本數據類型的時候曾經碰到過一個大哥大,曾覺得可以裝進去很大很大的整數,畢竟是8字節的身材,可是仔細那麼一比較,存儲範圍居然還比不過4字節的float,更不要說同等身材的double了。

  • long的存儲範圍:-2^63 ~ 2^63 - 1
  • float:-2^128 ~ 2^128
  • double:-2^1024 ~ 2^1024

以上數據只是表示一個量級,不能表明浮點數的精確範圍,不過這也足夠碾壓long類型了,以致於long類型能夠隱式轉換爲float,這就解決了咱們的一個疑問,爲何4字節的float存儲範圍比8字節的long類型還要大?天然是存儲方式不一樣。

相關文章
相關標籤/搜索