(一)IEEE754標準
IEEE 754 標準即IEEE浮點數算術標準,由美國電氣電子工程師學會(IEEE)計算機學會旗下的微處理器標準委員會發布。
以32位float數據爲例,在內存中的存儲形式是1bit的符號位(S),8bit表示指數部分(Exp),23表示小數部分的尾數(Fraction)。
表一 單精度浮點數在內存中存儲形式
1bit符號 |
8bit指數部分 |
23bit尾數 |
符號位——S取0時表示負數,取1時表示負數。
指數部分——使用所謂的偏正值形式表示,而不是補碼錶示,即指數部分採用一個無符號的正數值存儲。也就是說指數部分要表示的值等於實際存儲值減去一個固定值(對於單精度float類型,是127)。採用這種方式表示的目的是簡化比較,因爲,如果採用補碼錶示的話,全體符號位S和Exp自身的符號位將導致不能簡單的進行大小比較。因爲指數值的大小從0~255(0和255是特殊值),單精度的指數部分範圍是-127~+128(對應的,-127和128是特殊值)。
尾數部分——23bit尾數僅能表示小數部分的尾數,小數部分最高有效位由指數部分決定,具體見下表。小數部分最高有效位是1的數被稱爲正規形式。小數部分最高有效位是0的數被稱爲非正規形式,其他情況是特殊值。
表二 單精度浮點數表示規則
符號 |
指數 部分 |
指數部分-127 |
尾數部分 |
小數部分的 最高有效位 |
形式 |
1 |
255 |
128 |
非0 |
沒有 |
NaN |
1 |
255 |
128 |
0 |
沒有 |
負無窮 |
1 |
1~254 |
-126~127 |
任意 |
1 |
正規形式(負數) |
1 |
0 |
-127 |
非0 |
0 |
非正規形式(負數) |
1 |
0 |
-127 |
0 |
沒有 |
負0 |
0 |
0 |
-127 |
0 |
沒有 |
正0 |
0 |
0 |
-127 |
非0 |
0 |
非正規形式(正數) |
0 |
1~254 |
-126~127 |
任意 |
1 |
正規形式(正數) |
0 |
255 |
128 |
0 |
沒有 |
正無窮 |
0 |
255 |
128 |
非0 |
沒有 |
NaN |
按照IEEE標準,除了 NaN 以外,浮點數集合中的所有元素都是有序的。如果把它們從小到大按順序排列好,那順序將會是:負無窮,正規形式(負數)、非正規形式(負數)、負0、正0、非正規形式(正數)、正規形式(正數)、正無窮。
對於64bit的雙精度double類型,在內存中的存儲形式是1bit的符號位(S),11bit表示指數部分(Exp),52bit表示小數部分的尾數(Fraction)。指數部分的偏正值是1023,其他情況跟單精度類似,不再贅述。
(二)浮點數Q&A
1)浮點數可以表示數據的範圍是什麼?
不考慮特殊值(無窮大、NaN等),浮點數可以表示的範圍是[-Max,Max]。其中Max是浮點數能表示的最大值,具體值參見表三。
表三 浮點數最大值
浮點類型 |
字節碼 |
16進製表示 |
10進製表示 |
單精度 |
7f7fffff |
0x1.fffffep127 |
3.4028235E38 |
雙精度 |
7fefffffffffffff |
0x1.fffffffffffffp1023 |
1.7976931348623157E308 |
2)浮點數的精度怎樣衡量?
浮點數指數部分Exp的數值決定了浮點數與相鄰浮點數的差值,所以,指數部分越小(單精度最小爲-127),即浮點數絕對值越小(也就是浮點數越靠近0),相鄰浮點數的差值越小(單精度最小爲2^(-127)),浮點數能表示的有效小數位數越多。反之,指數部分越大(單精度最大爲127),即浮點數絕對值越大(也就是浮點數越遠離0),相鄰浮點數的差值越大(單精度最小爲2^(127)),浮點數能表示的有效小數位數越少。但是,從科學計算的角度看,不管指數部分的數值是多少,浮點數的有效位數由尾數部分決定,單精度的有效數是7位,雙精度的有效數是16位。
表四 浮點數最小正數
浮點類型 |
字節碼 |
16進製表示 |
10進製表示 |
單精度最小正數 |
00000001 |
0x0.000002p-126 |
1.4E-45 |
雙精度最小正數 |
0000000000000001 |
0x0.0000000000001p-1022 |
4.9E-324 |
3)我們知道,在Java中,存在基本數據類型的自動轉換,比如,直接將一個整形字面量賦給一個float變量。 那麼,在自動轉換後,整形的精度會丟失麼?
當浮點集中沒有與整形值對應的浮點數時,會將整形值轉化成最接近的浮點值,此時,整形值會丟失精度。例如下面的例子,數值爲33554431的整形轉化成單精度浮點數後,變成3.3554432E7,即33554432。
int intValue = Integer.MAX_VALUE >> 6;// 33554431
float floatFromInt = intValue;
System.out.println(floatFromInt);// 3.3554432E7
System.out.println(intValue);// 33554431
最後附查看某些浮點數字節碼、16進製表示、10進製表示的源碼及運行結果。
單精度源碼及結果。
1 package com.wsm.test;
2
3 public class TestFloat {
4
5 /**
6 * @param args
7 */
8 public static void main(String[] args) {
9
10 int intValue = Integer.MAX_VALUE >> 6;// 33554431
11 float floatFromInt = intValue;
12 System.out.println(floatFromInt);// 3.3554432E7
13 System.out.println(intValue);// 33554431
14
15 System.out.printf("%-12s\t%-20s%-12s%-20s\n", "描述", "十六進制數",
16 " 字節碼", " 十進制數");
17 print("正 無 窮", Float.POSITIVE_INFINITY);
18 print("最 大 值", Float.MAX_VALUE);
19 print("最小正規形式正數", Float.MIN_NORMAL);
20 print("最大非正規形式值", +0x0.fffffep-127f);
21 print("最 小 正 數", Float.MIN_VALUE);
22 print("負 無 窮", Float.NEGATIVE_INFINITY);
23 print("規 範 的 NaN", Float.NaN);
24 print("其 他 的 NaN", Float.intBitsToFloat(Integer
25 .valueOf(0xffc54321)));
26
27 }
28
29 static void print(String describe, float floatNum) {
30 System.out.printf("%-12s\t%-20s%-12s%-20s\n", describe, Float
31 .toHexString(floatNum), insertZero(Integer.toHexString(Float
32 .floatToRawIntBits(floatNum)), 8), floatNum);
33 }
34
35 static String insertZero(String input, int length) {
36 StringBuilder sb = new StringBuilder(input);
37 while (sb.length() < length) {
38 sb.insert(0, "0");
39 }
40 return sb.toString();
41 }
42 }
3.3554432E7
33554431
描述 十六進制數 字節碼 十進制數
正 無 窮 Infinity 7f800000 Infinity
最 大 值 0x1.fffffep127 7f7fffff 3.4028235E38
最小正規形式正數 0x1.0p-126 00800000 1.17549435E-38
最大非正規形式值 0x0.8p-126 00400000 5.877472E-39
最 小 正 數 0x0.000002p-126 00000001 1.4E-45
負 無 窮 -Infinity ff800000 -Infinity
規 範 的 NaN NaN 7fc00000 NaN
其 他 的 NaN NaN ffc54321 NaN
雙精度源碼及結果。
1 package com.wsm.test;
2
3 public class TestDouble {
4
5 /**
6 * @param args
7 */
8 public static void main(String[] args) {
9
10 System.out.printf("%-12s\t%-30s%-24s%-40s\n", "描述", "十六進制數",
11 " 字節碼",
12 " 十進制數");
13 print("正 無 窮", Double.POSITIVE_INFINITY);
14 print("最 大 值", Double.MAX_VALUE);
15 print("最小正規形式正數", Double.MIN_NORMAL);
16 print("最大非正規形式值", +0x0.fffffffffffffp-1023d);
17 print("最 小 正 數", Double.MIN_VALUE);
18 print("負 無 窮", Double.NEGATIVE_INFINITY);
19 print("規 範 的 NaN", Double.NaN);
20 print("其 他 的 NaN", Double.longBitsToDouble(Long
21 .valueOf(0xfff8000000054321L)));
22
23 }
24
25 static void print(String describe, Double DoubleNum) {
26 System.out.printf("%-12s\t%-30s%-24s%-40s\n", describe, Double
27 .toHexString(DoubleNum), insertZero(Long.toHexString(Double
28 .doubleToRawLongBits(DoubleNum)), 16), DoubleNum);
29 }
30
31 static String insertZero(String input, int length) {
32 StringBuilder sb = new StringBuilder(input);
33 while (sb.length() < length) {
34 sb.insert(0, "0");
35 }
36 return sb.toString();
37 }
38 }
描述 十六進制數 字節碼 十進制數
正 無 窮 Infinity 7ff0000000000000 Infinity
最 大 值 0x1.fffffffffffffp1023 7fefffffffffffff 1.7976931348623157E308
最小正規形式正數 0x1.0p-1022 0010000000000000 2.2250738585072014E-308
最大非正規形式值 0x0.8p-1022 0008000000000000 1.1125369292536007E-308
最 小 正 數 0x0.0000000000001p-1022 0000000000000001 4.9E-324
負 無 窮 -Infinity fff0000000000000 -Infinity
規 範 的 NaN NaN 7ff8000000000000 NaN
其 他 的 NaN NaN fff8000000054321 NaN
參考資料:
2、《Java虛擬機規範(Java SE 7)》