float類型的存儲


float類型的存儲

之前我們學過了Java的四種基本整數類型:

  • byte(1字節)

  • short(2字節)

  • int(4字節)

  • long(8字節)

其中一個字節是8位,所以能表示的個數就是28*x個(其中x表示字節數)

因為有正數和負數,所以范圍就是-28*x-1到​28*x-1-1,正數為什么要減一呢,因為有個0算正整數。

可能看起來不太簡潔但意思就是這個意思

而浮點數分別為float(4字節)和double(8字節)

也就是32位和64位

這在計算機中要怎么表示呢

無論是單精度還是雙精度在存儲中都分為三個部分:

  • 符號位(Sign) :0代表正,1代表為負

  • 指數位(Exponent):用於存儲科學計數法中的指數數據,並且采用移位存儲

  • 尾數部分(Mantissa):尾數部分

  首先我們知道常用科學計數法是將所有的數字轉換成(±)a.b x 10c 的形式,其中a的范圍是1到9共9個整數,b是小數點后的所有數字,c是10的指數。

而計算機中存儲的都是二進制數據,所以float存儲的數字都要先轉化成(±)a.bx2c,由於二進制中最大的數字就是1,所以表示法可以寫成(±)1.b x 2c的形式,float要想存儲小數就只需要存儲(±),b和c就可以了。

  float的存儲正是將4字節32位划分為了3部分來分別存儲正負號,小數部分和指數部分的:

  • Sign(1位):用來表示浮點數是正數還是負數,0表示正數,1表示負數。
  • Exponent(8位):指數部分。即上文提到數字c,但是這里不是直接存儲c,為了同時表示正負指數以及他們的大小順序,這里實際存儲的是c+127

  • Mantissa(23位):尾數部分。也就是上文中提到的數字b。

三部分在內存中的分布如下,用首字母代替類型

S E E E E E E E E M M M M M M M M M M M M M M M M M M M M M M
0 1 0 0 0 0 0 0 1 1 0 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1

float存儲示例

 以數字6.5為例,看一下這個數字是怎么存儲在float變量中的:

先來看整數部分,模2求余可以得到二進制表示為110。

再來看小數部分,乘2取整可以得到二進制表示為.1

小數轉換二進制方法:乘2取整法,即將小數部分乘以2,然后取整數部分,剩下的小數部分繼續乘以2,然后取整數部分,剩下的小數部分又乘以2,一直取到小數部分為零為止。如果永遠不能為零,就同十進制數的四舍五入一樣,按照要求保留多少位小數時,就根據后面一位是0還是1,取舍,如果是零,舍掉,如果是1,向入一位。換句話說就是0舍1入。讀數要從前面的整數讀到后面的整數。

例:將0.375換算為二進制
得出結果:將0.375換算為二進制(0.011)2
分析:第一步,將0.375乘以2,得0.75,則整數部分為0,小數部分為0.75;
第二步, 將小數部分0.75乘以2,得1.5,則整數部分為1,小數部分為0.5;
第三步, 將小數部分0.5乘以2,得1.0,則整數部分為1,小數部分為0.0;
第四步,讀數,從第一位讀起,讀出每一次的整數部分,讀到最后一位,即為0.011。)

拼接在一起得到110.1然后寫成類似於科學計數法的樣子,得到1.101 x  22

從上面的公式中可以知道符號為正,尾數是101,指數是2。

符號為正,那么第一位填0,指數是2,加上偏移量127等於129,二進制表示為10000001,填到2-9位,剩下的尾數101填到尾數位上即可

S E E E E E E E E M M M M M M M M M M M M M M M M M M M M M M
0 1 0 0 0 0 0 0 1 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

內存中二進制數01000000 11010000 00000000 00000000表示的就是浮點數6.5

float范圍

  明白了上面的原理就可求float類型的范圍了,找到所能表示的最大值,然后將符號為置為1變成負數就是最小值,要想表示的值最大肯定是尾數最大並且指數最大,那么可以得到尾數為 0.1111 1111 1111 1111 1111 111,指數為 1111 1111,但是指數全為1時有其特殊用途,所以指數最大為 11111110,指數減去127得到127,所以最大的數字就是1.1111111 1111111 11111111 x 2127,這個值為 340282346638528859811704183484516925440,通常表示成3.4028235E38 ,那么float的范圍就出來了:

[-3.4028235E38, 3.4028235E38]

float精度

  float 類型的數據精度取決於尾數,相信大家都知道這一點,但是精度怎么算我也是迷糊了好久,最近在不斷嘗試的過程中漸漸的明白了,首先是在不考慮指數的情況下23位尾數能表示的范圍是[0, ​223− 1],實際上尾數位前面還隱含了一個"1",所以應該是一共24位數字,所能表示的范圍是[0, ​224-1](因為隱含位默認是"1",所以表示的數最小是1不是0,但是先不考慮0,后面會特殊介紹,這里只按一般值計算),看到這里我們知道這24位能表示的最大數字為 ​224-1,換算成10進制就是16777215,那么[0, 16777215]都是能精確表示的,因為他們都能寫成1.b x​2c的形式,只要配合調整指數c就可以了。

16777215 這個數字可以寫成1.1111111 11111111 1111111 * 223,所以這個數可以精確表示,然后考慮更大的數16777216,因為正好是2的整數次冪,可以表示1.0000000 00000000 00000000 * 224,所以這個數也可以精確表示,在考慮更大的數字16777217,這個數字如果寫成上面的表示方法應該是 1.0000000 00000000 00000000 1 * 224,但是這時你會發現,小數點后尾數位已經是24位了,23位的存儲空間已經無法精確存儲,這時浮點數的精度問題也就是出現了。

  看到這里發現 16777216 貌似是一個邊界,超過這個數的數字開始不能精確表示了,那是不是所有大於16777216的數字都不能精確表示了呢?其實不是的,比如數字 33554432 就可以就可以精確表示成1.0000000 00000000 00000000 * 225,說道這里結合上面提到的float的內存表示方式,我們可以得出大於 16777216 的數字(不超上限),只要可以表示成小於24個2的n次冪相加,並且每個n之間的差值小於24就能夠精確表示。換句話來說所有大於 16777216 的合理數字,都是[0, 16777215]范圍內的精確數字通過乘以得到的,同理所有小於1的正數,也都是 [0, 16777215] 范圍內的精確數字通過乘以得到的,只不過n取負數就可以了。

  16777216 已經被證實是一個邊界,小於這個數的整數都可以精確表示,表示成科學技術法就是1.6777216 *107 ,從這里可以看出一共8位有效數字,由於最高位最大為1不能保證所有情況,所以最少能保證7位有效數字是准確的,這也就是常說float類型數據的精度。

float小數

  從上面的分析我們已經知道,float可表示超過16777216范圍的數字是跳躍的,同時float所能表示的小數也都是跳躍的,這些小數也必須能寫成2的n次冪相加才可以,比如0.5、0.25、0.125…以及這些數字的和,像5.2這樣的數字使用float類型是沒辦法精確存儲的,5.2的二進制表示為101.0011001100110011001100110011……最后的0011無限循環下去,但是float最多能存儲23位尾數,那么計算機存儲的5.2應該是101.001100110011001100110,也就是數字 5.19999980926513671875,計算機使用這個最接近5.2的數來表示5.2。關於小數的精度與剛才的分析是一致的,當第8位有效數字發生變化時,float可能已經無法察覺到這種變化了。

float特殊值

  我們知道float存儲浮點數的形式是(±)1.b x 2c ,因為尾數位前面一直是個1,所以無論b和c取什么樣的值,都無法得到0,所以在float的表示方法中有一些特殊的約定,用來表示0已經其他的情況。

  float的內存表示指數位數有8位,范圍是[0, 255],考慮偏移量實際的指數范圍是[-127,128],但實際情況下指數位表示一般數字時不允許同時取0或者同時取1,也就是指數位的實際范圍是[-126,127],而指數取-127和128時有其特殊含義,具體看下面表格:

符號位 指數位 尾數位 數值 含義
0 全為0 全為0 +0 正數0
1 全為0 全為0 -0 負數0
0 全為0 任意取值f 0.f∗2-126 非標准值,尾數前改為0,提高了精度
1 全為0 任意取值f 0.f∗2-126 非標准值,尾數前改為0,提高了精度
0 全為1 全為0 +Infinity 正無窮大
1 全為1 全為0 -Infinity 負無窮大
0/1 全為1 不全為0 NaN 非數字,用來表示一些特殊情況

總結

  1. float的精度是保證至少7位有效數字是准確的

  2. float的取值范圍[-3.4028235E38, 3.4028235E38],精確范圍是[-340282346638528859811704183484516925440, 340282346638528859811704183484516925440]

  3. 一個簡單的測試float精度方法,C++代碼中將數字賦值給float變量,如果給出警告warning C4305: “=”: 從“int”到“float”截斷,則超出了float的精度范圍,在我的測試中賦值為16777216及以下整數沒有警告,賦值為16777217時給出了警告。

 

PS:引用了一些csdn的,覺得有用就點個贊吧!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM