背景:float 底層用4個字節32位來表示,爲什麼範圍比int,long還要大?
整數在計算機底層採用補碼的形式表示,除去首位的符號位,剩餘的位數即表示數值的範圍,也就是位數越多,範圍越大,那麼對於單精度浮點數float,和雙精度浮點數double,它們底層的數據結構是什麼,它們的範圍又是如何計算的,它們的精度範圍又是多少那,接下來將從6方面進行闡述。
1 IEEE754浮點數標準
IEEE754標準包含一組實數的二進制表示法。它有三部分組成:
- 符號位
- 指數位
- 尾數位
三種精度的浮點數各個部分位數如下:
用float來舉例:
第一部分用來存儲符號位(sign),用來區分正負數,0正1負。
第二部分用來存儲指數(exponent),指數也有正負之分,指數位確定大小範圍。
第三部分用來存儲小數(fraction),小數位決定了計算精度,因爲小數位能表示的數越大,精度越大,數值越準確。
備註:浮點數在內存中是按科學計數法來存儲的,其整數部分始終是一個隱含着的“1”
2 第二部分(指數位確定範圍)
需要注意的是指數可能是負數,也有可能是正數,即指數是有符號整數,而有符號整數的計算是比無符號整數麻煩的。所以爲了減少不必要的麻煩,在實際存儲指數的時候,需要把指數轉換成無符號整數。
那麼怎麼轉換呢?注意到float的指數部分是8位,則指數的取值範圍是 -126到+127,爲了消除負數帶來的實際計算上的影響(比如比較大小,加減法等),可以在實際存儲的時候,給指數做一個簡單的映射,加上一個偏移量,比如float的指數偏移量爲127,對應的double類型,存儲的時候指數偏移量是1023,這樣就不會有負數出現了。
3 第三部分(小數位確定精度)
float和double的精度是由尾數的位數來決定的。浮點數在內存中是按科學計數法來存儲的,其整數部分始終是一個隱含着的“1”,由於它是不變的,故不能對精度造成影響。
float:2^23 = 8388608,一共七位,由於最左爲1的一位省略了,這意味着最多能表示8位數,但絕對能保證的爲7位,也即float的精度爲7~8位有效數字(算上整數的一位),不算整數位的話能只有7位,所以能準確表示的10進制也就6位。
double:2^52 = 4503599627370496,一共16位,同理,double的精度爲16~17位,能準確表示的10進制也就15位。
4 注意點
- 程序中應儘量避免浮點數的比較,例如System.out.println(2.00-1.10==0.90),永遠返回false.
- float、double類型的運算往往都不準確
5 正確的方式
- 利用整數來表示小數int或者long,例如1.01元,則可以以分爲單位來表示。
- 利用BigDecimal進行浮點型運算,但是注意一定要使用String類型的構造方法,如果使用浮點型構造方法也不能得到準確的結果。
6 福利彩蛋(浮點數保留2位的幾種方法)
1> double dd = (double) (Math.round(1/(float)num*100)/100.0); //先乘以100後除以100,對浮點數做進位處理。
2> DecimalFormat df=new DecimalFormat("###.00")
3>String ss = String.format("%1.2f", 1.1256);
4> NumberFormat ddf1=NumberFormat.getNumberInstance() ; ddf1.setMaximumFractionDigits(2);
5> BigDecimal b = new BigDecimal(T); T = b.setScale(2,BigDecimal.ROUND_HALF_UP).floatValue();