JVM支持IEEE-754浮點數標準(1985)。該標準定義了32位和64位浮點數的格式,以及在此之上的各種運算。在JVM中,浮點運算是基于32位float數和64位double數的。對每個操作float數的字節碼,都有一個對應的操作double數的版本。
浮點數由4部分組成:數符(sign),尾數(mantissa),基數(radix)和階碼(exponent)。數符取1或-1。尾數是一個正數,它持有浮點數的有效位。階碼是尾數和符號位應該乘以的基數的正(負)冪數。這4個部分用下面的公式得到浮點數的值:
sign * mantissa * radixexponent |
浮點數有很多種表現形式,因為你總是可以把浮點數的尾數乘以基數的某次冪,然后通過改變階碼的方式獲得原先的值。例如,-5可以寫成如下以10為基數的形式:
Sign |
Mantissa |
Radix |
Exponent |
-1 |
50 |
10 |
-1 |
-1 |
5 |
10 |
0 |
-1 |
0.5 |
10 |
1 |
-1 |
0.05 |
10 |
2 |
每個浮點數都有一個規范化的表示形式。如果浮點數的尾數符合下面的公式,我們就說這個浮點數是規范化的。
規范化的以10為基數的浮點數,尾數的小數點出現在第一個非0的有效位前面。-5的規范化形式是-1*0.5*10 1。也就是說,規范化的浮點數中,小數點的左邊是0,右邊第一位不是0。其他不是這種形式的浮點數都是非規范化數。注意,0沒有規范化的形式,因為它沒有非0數放在小數點后面。數字0們總是感嘆“為什么要規范化我們呢?”。
JVM中的浮點數以2為基數,所以JVM中的浮點數值用下面的公式獲得:
sign * mantissa * 2exponent |
JVM中的浮點數的尾數用2進制數表示。規范化的二進制數小數點出現在非0最高有效位前面。由于二進制數系統只有2個數字,1和0,所以最高有效位上的數字總是1。
float和double數的最高有效位是它的符號位,float的最后23位是尾數位,而double則為最后52位。階碼處在符號位和尾數中間,float為8位,double為11位。float的完整2進制形式如下,s表示符號位,e表示階碼,m表示尾數。
s eeeeeeee mmmmmmmmmmmmmmmmmmmmmmm |
正數的符號位為0,負數的符號位為1。尾數總是一個正的二進制數,但不是二進制補碼數。如果符號位為1,浮點數的值是負的,但尾數仍然是正的。
階碼有3種解釋方式。如果階碼位全是1,意味著這是一個特殊的浮點數,表示正無窮或負無窮,或者不是一個數(NaN)。NaN是特定運算的結果,比如除法的除數是0。階碼的所有位為0,表示這是一個非規范化的浮點數。除上面2種情況之外的階碼,都是規范化浮點數的一部分。
尾數部分其實隱式包含了一個額外的精度位。float數的尾數占23位,卻有24個精度位;同一樣的,double數的尾數占52位,卻有53個精度位。因為尾數部分的最高有效位是可以被預測的,所以并沒有包含在尾數中。JVM中,浮點數的階碼可以指明該數是不是一個規范化浮點數。如果階碼位全0,則為非規范化數,且最高有效位肯定是0。其他情況下,則為規范化浮點數,且最高有效位肯定是1。
在JVM中,任何浮點運算都不會拋出異常。類似除數為0的問題操作,JVM會返回一些特殊值,比如正/負無窮,或NaN。尾數位全是0的情況下,如果階碼位全是1,符號位是0,則表示正無窮;如果階碼位全是1,符號位是1,則表示負無窮。如果階碼位全是1,尾數位不全是0,則表示NaN。JVM總是為NaN使用相同的尾數:最高有效位是1,其他全為0。下表列出了上面提到的3中特殊值:
Special float values |
Float bits (sign exponent mantissa) |
+Infinity |
0 11111111 00000000000000000000000 |
-Infinity |
1 11111111 00000000000000000000000 |
NaN |
1 11111111 10000000000000000000000 |
非全0和非全1的階碼表示規范化尾數要乘以的2的冪數。可以把階碼當做一個正數,然后減去一個偏移量,這樣就能得到實際的冪數。對float數來說,偏移量是126;而double數,則為1023。例如,一個float數的階碼為00000001,則冪數為-125(1 – 126),這是float中最小的冪數。再看一個例子,如果階碼是11111110,則冪數為128(254 – 126),這是float中最大的冪數。下表列出了一些正規化的浮點數:
Normalized float values |
Float bits (sign exponent mantissa) |
Unbiased exponent |
Largest positive (finite) float |
0 11111110 11111111111111111111111 |
128 |
Largest negative (finite) float |
1 11111110 11111111111111111111111 |
128 |
Smallest normalized float |
1 00000001 00000000000000000000000 |
-125 |
Pi |
0 10000000 10010010000111111011011 |
2 |
階碼位全0,說明尾數沒有規范化,也隱含說明了最高有效為是0,而不是1。這種情況下,冪數為浮點數的最小冪數。對float來說,是-125。這意味著,規范化尾數乘以2 -125的浮點數,它的階碼為00000001,而非規范化尾數乘以2 -125的浮點數,它的階碼為00000000。階碼范圍底端的非規范化數修正值,使得下溢出較為平緩。如果最小階碼用來表示規范化數,下溢成0的最小數值會更大一些。 換句話說,讓最小階碼表示非規范化數,可以使浮點數能表示更小的數值。雖然非規范化數的精度沒有規范化數高,但是,這相對于階碼一旦達到最小規范化數值,浮點數就會下溢成0來說,非規范化數更好一些。
Denormalized float values |
Float bits (sign exponent mantissa) |
Smallest positive (non-zero) float |
0 00000000 00000000000000000000001 |
Smallest negative (non-zero) float |
1 00000000 00000000000000000000001 |
Largest denormalized float |
1 00000000 11111111111111111111111 |
Positive zero |
0 00000000 00000000000000000000000 |
Negative zero |
1 00000000 00000000000000000000000 |
本文譯自:Floating-point arithmetic