概述
Double遵循IEEE754算数标准
Double遵循此标准中的64位浮点数表示方式。从左到右具体为:
- 第一位为符号部,0表示正,1表示负
- 2~12位为指数部,用以存放具体数值的指数
- 13~64位为尾数部
其中指数部为11位,可以表示2048个数,为-1023~+1024,因为存在正负号,会导致运算比较困难,故标准中设置了一个偏移值,将指数加上偏移值后得到编码值存储在指数部中,利于计算和比较。其中偏移值为2^(x-1)-1,x为指数部的位数,此处为11,故偏移值为1023.得到的编码值范围为0~2047(其中0(11位全是0)和2047(11位全是1)为特殊情况,下面会提到).
规约数:规约数是指数的编码值(假设为Q),0<Q<2047时,所表示的数,此时尾数的个位将自动补全1.例如,十进制下的15用double存储时,可以分以下几步理解:
1.先将15化为2进制,为1111
2.将1111转化为二进制下的指数形式,为1.111* 2^3,故用double存储时,指数为3,加上偏移值后的指数部存储编码值为1026,尾数部为111(小数点前的1自动补全)
3.double中的值为0100 0000 0010 1110(后面还有48个0)
但光用规约数也会产生问题,例如,只用规约数的话正数最小值为1.0* 2^-1022,这会带来很大麻烦,如1.2*2^(-1022)-1.1*2^(-1022)即0.1*2^(-1022)将用Double表示时将为0,值得连续性也会在此处发生断崖式下降(大于1.0* 2^-1022的数,连续性为2^(-1074)),0也无法表示。故需要引入非规约数。
非规约数:指数部的编码值为0,且尾数部不全为0,此时尾数部的整数位将自动补全0.
当一个数为非规约数时,指数编码值为0,实际指数为-1022(没错,是-1022,不是-1023,需要加1,就是这么规定的),此时,double的连续性将处处相等,都为2^(-1074),并且任何两个double的和差都能用唯一的double值表示。
当指数部编码值为0,尾数部也为0时,double值为0,固0可以有两种表达方式(+0和-0)
当指数部编码为2047,尾部全为0时,表示无穷,尾部不是全为0时,表示NaN
此种设计下的double值比较十分方便,首先比较符号位,为0的一定大于为1的(正数大于负数),若相等,再比较指数位,因为存的编码值都为正数,所以编码值大的数一定大(符号位为0的情况下,为1则结果反一下),若还相等,最后比较尾数位,十分高效
源码:
/*实现Comparable接口使Doubel对象变成可比较的对象 */
public final class Double extends Number implements Comparable<Double> {
/*在java中将 1.0 /2.0 设置为正无穷大即 Infinity*/
public static final double POSITIVE_INFINITY = 1.0 / 0.0;
/* 与上面相反 负无穷大 即 -Infinity */
public static final double NEGATIVE_INFINITY = -1.0 / 0.0;
/* 将 0.0 /0.0 设置为一个非数字的字符串*/
public static final double NaN = 0.0d / 0.0;
/* 设置最大值为1.7976931348623157e+308*/
public static final double MAX_VALUE = 0x1.fffffffffffffP+1023; // 1.7976931348623157e+308
Double.longBitsToDouble(0x0010000000000000L)}.
/* 保存 double 类型的最小正标准值的常量,最小正标准值为 2-1022。它等于十六进制的浮点字面值 0x1.0p-1022,也等于Double.longBitsToDouble(0x0010000000000000L)。 */
public static final double MIN_NORMAL = 0x1.0p-1022;
/*指数部最大值为2^1023 */
public static final int MAX_EXPONENT = 1023;
/*指数部最小值为2^-1022 */
static final int MIN_EXPONENT = -1022;
/*设置 长度为64位*/
public static final int SIZE = 64;
/* 设置为 8个字节*/
public static final int BYTES = SIZE / Byte.SIZE;
/*表示基本类型 double 的 Class 实例。 */
@SuppressWarnings("unchecked")
public static final Class<Double> TYPE = (Class<Double>) Class.getPrimitiveClass("double");
}
重点方法
toHexString(double d)将Double的值转换成十六进制,并返回成字符串
public static String toHexString(double d) { if (!isFinite(d) ) //判断是否超过double的最大值与最小值 return Double.toString(d);//如果超出了范围就返回他的字符串 else { StringBuilder answer = new StringBuilder(24);//声明了一个StringBuilder if (Math.copySign(1.0, d) == -1.0) //通过Math.copySign 返回的值来判断传入的double值是否为负数,如果是负数就是在上面的StringBuilder中添加一个负号 answer.append("-"); answer.append("0x");//十六进制以0x开头 d = Math.abs(d);//将传入的值进行绝对值转换要是负数的话就变成整数,整数不变 if(d == 0.0) { answer.append("0.0p0");//如果传入的值为0.0直接返回0x0.0p0 } else { //传进来的值不为0.0 boolean subnormal = (d < DoubleConsts.MIN_NORMAL);//设置一个布尔变量 判断是否小于DoubleConsts最小值 long signifBits = (Double.doubleToLongBits(d) & DoubleConsts.SIGNIF_BIT_MASK) | 0x1000000000000000L; answer.append(subnormal ? "0." : "1."); /*隔离低位十六进制的13位数字*/ String signif = Long.toHexString(signifBits).substring(3,16); answer.append(signif.equals("0000000000000") ? // 13 zeros "0": signif.replaceFirst("0{1,12}$", "")); answer.append('p'); answer.append(subnormal ? DoubleConsts.MIN_EXPONENT: Math.getExponent(d)); } return answer.toString(); } }