进制转换

先来了解一下double类型的进制转换,示例:double类型数据:17.625

整数部分: 除以2,取余数,余数反向取

计算 余数 顺序
17÷2 8 1 5
8÷2 4 0 4
4÷2 2 0 3
2÷2 1 0 2
1÷2 0 此时商为0,不再计算 1 1

小数部分: 乘以2,积 >= 1 则为1,积 < 1,则为0,结果正向取

计算 结果a a>=1?1:0 顺序
0.625×2 1.25 1 1
0.25×2 0.5 0 2
0.5×2 1 1 3
0×2 此时因数为0,不再计算 0 0 4 最后一位都是0,不要也行

结果:

此时按照整数和小数各自的顺序,得出的17.625的二进制就是:10001.101

当然,这是正常的一个数字,double类型可以根据自身的底层规则,来存储这个数字,那么,double底层是怎样存储的呢?

为什么说它是正常的数字呢,因为还有其他数据,double类型并不能很精确的将其表示出来,可以详细看这篇文章Java中double类型,为啥会出现精度不准确的情况?

double的存储结构

double存储结构

科学计数法

除此之外,我们还需要知道科学计数法,科学计数法一般是小数点前只有一位整数,小数点后的使用多少次方来表示,看看下面的例子即可了解:

十进制 二进制 科学计数法 移动
17.625 10001.101 1.00011011041.0001101 * 10^4 向右移动了4位
2.5 10.1 1.011011.01 * 10^1 向右移动了1位
10.25 1010.01 1.010011031.01001 * 10^3 向右移动了3位

科学计数法中,存在三个数值位:底数,指数,符号

1.00011011041.0001101 * 10^4 为例:

尾数:以上可以看出,double类型的数字的二进制中,使用科学计数法来表示时,第一位,必然是1,所以根据IEEE规定,只需要记录小数点后面的就好了,所以底数为:0001101

指数:从科学计数法的表示中可以看出,为 4,但在实际上在存储的时候,是加上了1023的,所以为1027,二进制表示就为 1027 的二进制写法 10000000011

PS:为啥要加上偏移量 1023

指数偏移值(exponent bias),即浮点数表示法中指数域的编码值,等于指数的实际值加上某个固定的值,IEEE 754标准规定该固定值为 2e112^{e-1}-1 其中的 ee 为存储指数的比特的长度。

来自维基百科-IEEE标准

所以,根据上图中,double的存储结构可知:ee11,计算出的指数即为:2e11=21111=2101=10241=10232^{e-1}-1 = 2^{11-1}-1=2^{10}-1 = 1024-1 = 1023

符号:17.625是整数,所以符号位是 0

综上所述:17.625的double存储方式表示为:

符号位 + 指数位 + 尾数位

0 + 10000000011 + 0001101 + 45个0

这就是一个正常的double的数据在内存中的存储方式

验证:

通过java代码可以验证,注意,代码中 toBinaryString 方法,如果是正数,就不打印符号位,如果是负数,才把符号位打印出来。

public class Test {
    public static void main(String[] args) {
        double test = 17.625d;
        long testBits = Double.doubleToLongBits(test);
        System.out.println(Long.toBinaryString(testBits));
    }
}

1648899671774

扩展:

负数时,会将符号位打印出来。

1648899905272