轉自:https://blog.csdn.net/sky1988818/article/details/86539119
背景: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();
7 個人補充說明(非轉載部分)
float的范圍為-2^128 ~ +2^128,也即-3.40E+38 ~ +3.40E+38;double的范圍為-2^1024 ~ +2^1024,也即-1.79E+308 ~ +1.79E+308。
如果使用sprintf_s將數字以十進制格式化到字符串中,float類型數據需要至少定義成長度為42的字節數組,其中以-3.40E+38為極限長度,字符串格式形於“-0.0000.....34”,如char floatBuffer[43]。double類型數據需要至少定義成長度314的字節數組,如char doubleBuffer[314];
這一條是我在測試PLC數據讀取碰到的問題,即使用sprintf_s將float格式化成字符串輸出,在模擬環境中能正常運行,但是接入PLC系統后總是報Buffer Is Too Small,這是典型的字符串長度超出緩沖區問題,分析log我發現定義的Buffer長度為32個字節,但是格式化float成字符串的長度是52,問題顯而易見。部分log如下:
1 2020-04-10 18:37:12.836 [I] MachineNo = [0] 2 2020-04-10 18:37:12.838 [I] ACT_CHARGE_ID = [] 3 2020-04-10 18:37:12.841 [I] MachineStatus = [2] 4 2020-04-10 18:37:12.843 [I] ACT_PROCESS = [-28671] 5 2020-04-10 18:37:12.846 [I] ACT_CRU_ID = [] 6 2020-04-10 18:37:12.848 [I] ACT_INGOT_ID = [] 7 2020-04-10 18:37:12.851 [I] ACT_RCP_NAME = [] 8 2020-04-10 18:37:12.853 [I] Header = [0] 9 2020-04-10 18:37:12.856 [I] Timestamp = [] 10 2020-04-10 18:37:12.858 [I] User_Name = [] 11 2020-04-10 18:37:12.860 [I] ACT_XT_SPD = [0.000000] 12 2020-04-10 18:37:12.863 [I] ACT_XT_ROT = [0.000000] 13 2020-04-10 18:37:12.866 [I] ACT_XT_POS = [7046656236157048700000.000000] 14 2020-04-10 18:37:12.870 [I] ACT_CR_SPD = [0.000000] 15 2020-04-10 18:37:12.873 [I] ACT_PRS_CLF = [2245535288003289000000000000000000000.000000] 16 2020-04-10 18:37:12.878 [I] ACT_CR_ROT = [0.000000] 17 2020-04-10 18:37:12.885 [I] ACT_CR_POS = [4242751136953196900000.000000] 18 2020-04-10 18:37:12.889 [I] ACT_TOT_XT_LGH = [0.000000] 19 2020-04-10 18:37:12.893 [I] ACT_HTR_1_PWR = [0.000000] 20 2020-04-10 18:37:12.897 [I] ACT_HTR_1_TMP = [0.000000] 21 2020-04-10 18:37:12.900 [I] ACT_HTR_2_PWR = [0.000000] 22 2020-04-10 18:37:12.904 [I] ACT_MAG_1_CUR = [0.000000] 23 2020-04-10 18:37:12.907 [I] ACT_MAG_2_CUR = [0.000000] 24 2020-04-10 18:37:12.910 [I] ACT_GAS_1_FLW = [0.000000] 25 2020-04-10 18:37:12.913 [I] ACT_GAS_2_FLW = [0.000000] 26 2020-04-10 18:37:12.916 [I] ACT_PRS_CGT = [-0.000000] 27 2020-04-10 18:37:12.919 [I] FAULT[2] = [409] 28 2020-04-10 18:37:12.922 [I] ACT_XT_DIA = [0.000000] 29 2020-04-10 18:37:12.925 [I] ACT_XT_LEN_MM = [0.000000] 30 2020-04-10 18:37:12.929 [I] CRU_QRZ_TRANS_RAD = [0.000000] 31 2020-04-10 18:37:12.932 [I] CHARGE_WGT = [0.000000] 32 2020-04-10 18:37:12.935 [I] FAULT[1] = [410] 33 2020-04-10 18:37:12.937 [I] CRU_QRZ_WALL_THICKN = [0.000000] 34 2020-04-10 18:37:12.941 [I] FAULT[3] = [397] 35 2020-04-10 18:37:12.944 [I] CRU_QRZ_BOT_THICKN = [0.000000] 36 2020-04-10 18:37:12.947 [I] CRU_GRAPH_BOT_RAD = [-0.000000] 37 2020-04-10 18:37:12.951 [I] CRU_GRAPH_INNER_DIA = [36749372959343247000.000000] 38 2020-04-10 18:37:12.955 [I] FAULT0 = [0] 39 2020-04-10 18:37:12.958 [I] FAULT[4] = [398] 40 2020-04-10 18:37:12.961 [I] FAULT[5] = [405] 41 2020-04-10 18:37:12.963 [I] FAULT[6] = [404] 42 2020-04-10 18:37:12.966 [I] FAULT[7] = [401] 43 2020-04-10 18:37:12.968 [I] FAULT[8] = [400] 44 2020-04-10 18:37:12.971 [I] FAULT[9] = [416] 45 2020-04-10 18:37:12.973 [I] FAULT[10] = [135]