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