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 mask 0x7ff0000000000000L) represent the exponent. Bits 51-0 (the bits that are selected by the mask 0x000fffffffffffffL) 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;

posted @ 2019-12-13 18:21  zhangxuezhi  阅读(1703)  评论(0编辑  收藏  举报