java 浮点数表示法
这个要从Double类的一个方法说起:Double.doubleToLongBits(double value),根据官方文档,其部分注释内容如下:
public static long doubleToLongBits(double value)Returns a representation of the specified floating-point value according to the IEEE 754 floating-point "double format" bit layout.Bit 63 (the bit that is selected by the mask
0x8000000000000000L
) represents the sign of the floating-point number. Bits 62-52 (the bits that are selected by the mask0x7ff0000000000000L
) represent the exponent. Bits 51-0 (the bits that are selected by the mask0x000fffffffffffffL
) represent the significand (sometimes called the mantissa) of the floating-point number.
个人翻译一下:
根据IEEE 754浮点“double格式” 比特位的布局,返回一个浮点值。
bit63表示符号位,bits62-52表示指数域,bits51-0表示有效数字(尾数域)
关于 IEEE 754,可以参考该博客:https://blog.csdn.net/m0_37972557/article/details/84594879
现在做如下测试:
System.out.println(Double.doubleToLongBits(0.75));
System.out.println(Double.doubleToLongBits(0.5));
结果
0.75
十进制值:4604930618986332160
十六进制值:3FE8 0000 0000 0000
0.5
十进制值:4602678819172646912
十六进制值:3FE0 0000 0000 0000
对于0.75的十六进制,其符号位为0,指数域为3FE,尾数域为 8 0000 0000 0000
计算过程:
指数域0x3FE的十进制为:1022,而指数域的偏移码是:1023,因此实际的指数值为:1022 -1023 = -1
对于尾数域,后面的一堆0不看,最左边的值就是1,则表示其有效值为0.1,而尾数域的正规表示形式为:1.xxx(最左边的1在转换为浮点表示时会自动省略),因此尾数的实际值的计算方法是其有效值加1,即:1 + 0.1 = 1.1,该结果是二进制值
通过上面两步,拿到了指数为-1, 位数为1.1(二进制)
结果计算3种方法,结果一致,都是0.75
小数点移位法:指数域为-1,表示尾数1.1的小数点往左移动一位,得出结果: 0.11,转换成十进制就是:0.5 + 0.25 = 0.75
二进制相乘:指数域为-1,表示实际的指数结果为:2-1,即0.5,二进制表示为0.1,计算:1.1 * 0.1 = 0.11,十进制即0.75
十进制相乘:指数域为-1,表示实际的指数结果为:2-1,即0.5,而1.1的十进制表示为1.5,计算:1.5 * 0.5 = 0.75
同理:
0.5的十六进制中的有效数值3FE0,算出来指数域是-1,尾数域是0,结果为:0.1,转为十进制:0.5
可以类推:
0.25的浮点表示为:0x3FD0 0000 0000 0000
-0.25的浮点表示为:0xBFD0 0000 0000 0000
0.625的浮点表示为:0x3FE4 0000 0000 0000
1.5的浮点表示为:0x3FF8 0000 0000 0000
-3.625的浮点表示为:0xC00D 0000 0000 0000
再来看下最开始提到的方法的源码:
public static long doubleToLongBits(double value) { long result = doubleToRawLongBits(value); // Check for NaN based on values of bit fields, maximum // exponent and nonzero significand. if ( ((result & DoubleConsts.EXP_BIT_MASK) == DoubleConsts.EXP_BIT_MASK) && (result & DoubleConsts.SIGNIF_BIT_MASK) != 0L) result = 0x7ff8000000000000L; return result; } public static native long doubleToRawLongBits(double value);
该方法的核心其所调用的native方法,但是还额外处理了一类特殊值:当指数域的值达到1024,即超出规定的1023值,并且尾数域不为0时,此时会返回一个固定值。在IEEE 754中,这样的值是NaN,即not a number,不是一个数。
除此之外,IEEE 754还规定了另外两个特殊值:正无穷和负无穷。对应的形式:指数域为1024,尾数域为0,符号位为正则正无穷,为负则负无穷。
除此之外,Double类还提供一个逆过程的方法如下,可以传入浮点表示法的值(8个字节,所以要用long类型接收),然后返回实际的double值
public static native double longBitsToDouble(long bits);
这也是一个native方法,不知道是C++支持还是操作系统支持的这个方法。
因为双精度浮点表示的指数最大值是1023,因此其可表示的最大值为:21023 * 0x1.FFFFFFFFFFFFF(13个F),约等于:1.7976931348623157e+308
在jdk1.8的源码中,其表示如下:
public static final double MAX_VALUE = 0x1.fffffffffffffP+1023;