float為什么比int表示的范圍廣?
什么是單精度和雙精度?
float表示小數的時候為什么會有精度丟失?
帶着這幾個問題,我們來探究下java中float類型在計算機的表示形式。
java中int占用4個字節,float也是占用4個字節,但是為什么float表示的范圍要比int大呢,因為兩者在計算機內中表示的方式不一樣,int是4個字節32位,每一位都是二進制小數表示,最高位0代表是正數,最高位為1代表是負數,所以int的范圍是-2^31~2^31-1;
float也是4個字節,遵循IEEE-754格式標准,在計算機中表示有三個部分組成:符號s底數m和指數e。
符號s:最高位,0為正數;1為負數,占用一位;
指數e:是該浮點數的指數,二進制表示,最高位為指數符號為1表示大於1,0表示小於1,其實他是用的偏移量表示的,偏移大小為127,沒有用補碼表示,占用一個字節8位;
底數m:是該浮點數的實際值,二進制形式表示,m的范圍是【1,2】或者【0,1】占用23位。
所以float在計算機的表示如下面所示:
SEEEEEEEEMMMMMMMMMMMMMMMMMMMMMMM
我們先說下10進制和二進制的快速轉換方法。
10進制整數2進制轉換是除2求商和余數,比如十進制17的二進制計算為:
17 /2=16余1
16 /2= 8 余0
8 /2= 4 余0
4 /2= 2 余0
2 /2= 1 余0
1 /2= 0 余1
一直計算到商為0為止,17的二進制表示就是:10001
10進制小數2進制表示是乘2求積,比如0.16的二進制計算為:
0.16*2=0.32取0
0.32*2=0.64取0
0.64*2=1.28取1
0.28*2=0.56取0
0.56*2=1.12取1
0.12*2=0.24取0
。。。
所以0.16的二進制表示就是001010..... 一直循環下去,所以除非小數最后為5才能*2取整,否則都會無線循環下去,這就是為什么浮點小數精度丟失的問題。
下面我們距離來說明10進制浮點數在計算機的表示, 拿17.16來說:
1.首先為正數,s為0
2.二進制為10001.0010100011110101....小數點左移4位為1.00010010100011110101...
3.指數位為4+127=10000011,所以指數位位10000011
4.由於默認最左邊為1,所以底數為00010010100011110101...
5.最后17.16的二進制表示就是:01000001100010010100011110101......總共32位,后面的舍棄
再拿0.16舉例:
1.首先為整數,s為0
2.二進制為0.0010100011110101......小數點右移4位為1.0100011110101
3.指數位為4+127=10000011,然后取反為01111100,所以指數位位01111100
4.底數位0100011110101...
5.最后0.16的二進制表示就是:0011111000100011110101...
float的(正數)范圍:
最小值:Float.MIN_VALUE=1.4E-45 (2的-149次方:指數位127+底數22)
最大值:Float.MAX_VALUE=3.4028235E38 (2的128次方-1)
float是單精度浮點數,4個字節;double是雙精度浮點數,8個字節,e占11位,m占52位。
注意事項:
1.價格在數據庫中保存建議是無符號整形,避免在java轉換成浮點數時出現精度丟失的問題;
2.如果需要用浮點型數據進行運算,建議使用BigDecimal,但是BigDecimal的構造函數一定要使用字符串的,否則一樣會出現精度丟失的問題:BigDecimal(“123f”)而不是BigDecimal(123f)。
3.Float.MIN_VALUE是:1.4E-45,不是一個負數,double也是的。但是整型的MIN_VALUE都是負數
4.float的m只有23位,2^23=8388608,總共有7位10進制數字,由於最左邊的1省略了,所以表示的有效數字最高精確度位7~8位,包括整數部分,8位后的數字肯定不是准確的