此次来看看Double
的源代码,基于 jdk1.8.0_181.jdk 版本,若有错误,欢迎联系指出。html
Double
是double
基础数据类型的包装类,而double
是IEEE 754
标准的双精度 64bit 的浮点数,具体IEEE 754
标准的一些信息这里就再也不详细的介绍了,建议能够看看个人上一篇文章 [Java源码]Float 对此有个大体的了解,二者的逻辑基本是一致的。Java中对于十进制浮点数,double
是默认的类型。java
继续使用相似的图说明下, 程序员
1
位。0表示正数,1表示负数。11
位。双精度的指数部分是−1022~+1024加上偏移值1023,指数值的大小从1~2046(0和2047是特殊值)52
位。public final class Double extends Number implements Comparable<Double> 复制代码
定义中带有final
标识,表示是不可继承的
,另外继承了Number
类,实现了Comparable
接口编程
public static final double POSITIVE_INFINITY = 1.0 / 0.0;
public static final double NEGATIVE_INFINITY = -1.0 / 0.0;
public static final double NaN = 0.0d / 0.0;
复制代码
POSITIVE_INFINITY
表示正无穷,值为0x7ff0000000000000L
;标准定义指数域全为1,尾数域全为0NEGATIVE_INFINITY
表示负无穷,值为0xfff0000000000000L
;标准定义指数域全为1,尾数域全为0NaN
英文缩写,Not-a-Number
,标准定义为 指数域全为1,尾数域不全为0public static final double MAX_VALUE = 0x1.fffffffffffffP+1023; // 1.7976931348623157e+308
public static final double MIN_NORMAL = 0x1.0p-1022; // 2.2250738585072014E-308
public static final double MIN_VALUE = 0x0.0000000000001P-1022; // 4.9e-324
复制代码
MAX_VALUE
最大规约数为0x1.fffffffffffffP+1023
,这里是十六进制浮点数表示,也就是0x7fefffffffffffffL
,也是(2 - Math.pow(2, -52) * Math.pow(2, 1023))
,计算值为1.7976931348623157e+308
MIN_NORMAL
最小的规约数为0x1.0p-1022
,这里是十六进制浮点数表示,也就是0x0010000000000000L
,也是Math.pow(2, -1022)
,计算值为2.2250738585072014E-308
MIN_VALUE
最小非规约数为0x0.0000000000001P-1022
,这里是十六进制浮点数表示,也就是0x1L
,也是Math.pow(2, -1074)
,计算值为4.9e-324
public static final int MAX_EXPONENT = 1023;
public static final int MIN_EXPONENT = -1022;
复制代码
MAX_EXPONENT
表示了最大的指数值,为1023MIN_EXPONENT
表示了最小的指数值,为-1022
public static final int SIZE = 64;
复制代码
定义了 bit
位数oracle
public static final int BYTES = SIZE / Byte.SIZE;
复制代码
定义了Double
对象的字节数,计算值固定为8app
@SuppressWarnings("unchecked")
public static final Class<Double> TYPE = (Class<Double>) Class.getPrimitiveClass("double");
复制代码
获取类信息,Double.TYPE == double.class
二者是等价的less
private final double value;
复制代码
Double
是double
的包装类,这里存放了对应的double
数据值编程语言
private static final long serialVersionUID = -9172774392245257468L;
复制代码
public Double(double value) {
this.value = value;
}
public Double(String s) throws NumberFormatException {
value = parseDouble(s);
}
复制代码
能够传入double
或者String
类型参数,String
参数的构造方法内部会调用parseDouble
方法进行处理。ide
public static double parseDouble(String s) throws NumberFormatException {
return FloatingDecimal.parseDouble(s);
}
复制代码
内部调用了FloatingDecimal.parseDouble
实现具体逻辑,其中具体的处理过程和Float
相似,能够查看 parsefloat 方法 了解,这里就再也不重复叙述了。结果返回对应的double
类型数据值。post
public static String toString(double d) {
return FloatingDecimal.toJavaFormatString(d);
}
复制代码
依然是调用了FloatingDecimal.toJavaFormatString
的方法,处理过程和Float
也基本一致,结果返回对应的字符串格式。
public static String toHexString(double d) {
// 判断是不是有限数值
if (!isFinite(d) )
// 对于 infinity 和 NaN, 直接调用 toString 返回
return Double.toString(d);
else {
// 使用最大输出长度初始化StringBuilder容量
StringBuilder answer = new StringBuilder(24);
// 负数,增长符号标识
if (Math.copySign(1.0, d) == -1.0)
answer.append("-");
answer.append("0x");
d = Math.abs(d);
// 若是是0.0,直接输出返回
if(d == 0.0) {
answer.append("0.0p0");
} else {
// 判断是否为非规约数
boolean subnormal = (d < DoubleConsts.MIN_NORMAL);
// DoubleConsts.SIGNIF_BIT_MASK = 0x000FFFFFFFFFFFFFL
// & 操做保留尾数位数据
// | 操做是将最高位设为1,为了保留指数位的0,保留原来的长度,由于是个long类型整数
long signifBits = (Double.doubleToLongBits(d)
& DoubleConsts.SIGNIF_BIT_MASK) |
0x1000000000000000L;
// 规约数为1.开头,非规约数为0.开头
answer.append(subnormal ? "0." : "1.");
// 使用Long.toHexString获取十六进制字符串,提取尾数位对应的字符串信息
// 判断若是全为0,使用一个0替换
// 如若不是,去除字符串尾部的全部0
String signif = Long.toHexString(signifBits).substring(3,16);
answer.append(signif.equals("0000000000000") ? // 13 zeros
"0":
signif.replaceFirst("0{1,12}$", ""));
answer.append('p');
// DoubleConsts.MIN_EXPONENT = -1022
// 若是是非规约数,使用最小的指数位替换
// 规约数,获取对应的指数值替代
answer.append(subnormal ?
DoubleConsts.MIN_EXPONENT:
Math.getExponent(d));
}
return answer.toString();
}
}
复制代码
总体的逻辑在代码注释中进行了说明,清晰且简单,结果返回对应的十六进制字符串。
public static Double valueOf(double d) {
return new Double(d);
}
public static Double valueOf(String s) throws NumberFormatException {
return new Double(parseDouble(s));
}
复制代码
存在两个valueOf
方法,当参数为double
类型时,直接new Double(d)
而后返回;对于字符串参数,调用parseDouble
转换成double
数据值,而后new一个新对象返回。
public static boolean isNaN(double v) {
return (v != v);
}
public boolean isNaN() {
return isNaN(value);
}
复制代码
判断是不是NaN
,使用(v != v)
判断;具体NaN
的规则描述能够参考 isNaN 方法
public static boolean isInfinite(double v) {
return (v == POSITIVE_INFINITY) || (v == NEGATIVE_INFINITY);
}
public boolean isInfinite() {
return isInfinite(value);
}
复制代码
判断是否是无穷数,包含正无穷和负无穷
public static boolean isFinite(double d) {
return Math.abs(d) <= DoubleConsts.MAX_VALUE;
}
复制代码
经过输入参数绝对值是否小于double
类型的最大值,判断是否是有限数
public byte byteValue() {
return (byte)value;
}
public short shortValue() {
return (short)value;
}
public int intValue() {
return (int)value;
}
public long longValue() {
return (long)value;
}
public float floatValue() {
return (float)value;
}
public double doubleValue() {
return value;
}
复制代码
返回对应类型的值,直接进行强制类型转换
@Override
public int hashCode() {
return Double.hashCode(value);
}
public static int hashCode(double value) {
long bits = doubleToLongBits(value);
return (int)(bits ^ (bits >>> 32));
}
复制代码
>>>
为无符号右移,高位以0补齐。(bits ^ (bits >>> 32))
逻辑为高32位与低32位异或计算返回int
整数值做为hashCode
public static native double longBitsToDouble(long bits);
复制代码
longBitsToDouble
是个native
方法,由c代码实现。返回对应double
数据值
参数为0x7ff0000000000000L
时,结果为正无穷
参数为0xfff0000000000000L
时,结果为负无穷
参数在0x7ff0000000000001L ~ 0x7fffffffffffffffL
或者0xfff0000000000001L ~ 0xffffffffffffffffL
之间时,结果为NaN
。
public static native long doubleToRawLongBits(double value);
复制代码
doubleToRawLongBits
是个native
方法,由对应的c代码实现。
结果会保留NaN
值,正无穷结果为0x7ff0000000000000L
;负无穷结果为0xfff0000000000000L
;当参数为NaN
时,结果会是输入参数对应的实际整数值,该方法不会像doubleToLongBits
,对NaN
进行统一的返回值处理
public static long doubleToLongBits(double value) {
long result = doubleToRawLongBits(value);
if ( ((result & DoubleConsts.EXP_BIT_MASK) ==
DoubleConsts.EXP_BIT_MASK) &&
(result & DoubleConsts.SIGNIF_BIT_MASK) != 0L)
result = 0x7ff8000000000000L;
return result;
}
复制代码
基本与doubleToRawLongBits
方法一致,只是增长了对NaN
的判断。如果NaN
则直接返回0x7ff8000000000000L
(对全部的NaN
值进行了统一返回值处理)。这里识别NaN
的逻辑符合指数域全为1,尾数域不全为0的标准规范,具体说明能够参考 floatToIntBits 方法 说明。
public boolean equals(Object obj) {
return (obj instanceof Double)
&& (doubleToLongBits(((Double)obj).value) ==
doubleToLongBits(value));
}
复制代码
首先判断是否是Double
对象实例,而后经过doubleToLongBits
获取两个对应的长整型数,判断二者是否一致;值得注意的是一些特殊值的判断逻辑。
System.out.println(new Double(0.0d).equals(new Double(-0.0d))); // false
System.out.println(new Double(Double.NaN).equals(new Double(-Double.NaN))); // true
复制代码
public int compareTo(Double anotherDouble) {
return Double.compare(value, anotherDouble.value);
}
public static int compare(double d1, double d2) {
if (d1 < d2)
return -1; // Neither val is NaN, thisVal is smaller
if (d1 > d2)
return 1; // Neither val is NaN, thisVal is larger
// Cannot use doubleToRawLongBits because of possibility of NaNs.
long thisBits = Double.doubleToLongBits(d1);
long anotherBits = Double.doubleToLongBits(d2);
return (thisBits == anotherBits ? 0 : // Values are equal
(thisBits < anotherBits ? -1 : // (-0.0, 0.0) or (!NaN, NaN)
1)); // (0.0, -0.0) or (NaN, !NaN)
}
复制代码
与Float
一致,能够参考 compare方法 段落说明,须要注意的依然是-0.0
,0.0
,Double.NaN
和-Double.NaN
这类的特殊值,能够自行编写几个进行测试一下。
public static double sum(double a, double b) {
return a + b;
}
public static double max(double a, double b) {
return Math.max(a, b);
}
public static double min(double a, double b) {
return Math.min(a, b);
}
复制代码
逻辑很简单,依旧须要注意的是-0.0
,0.0
,Double.NaN
和-Double.NaN
这类的特殊值,能够自行测试下结果,也许会出乎你的意料哦。
由于double
是64bit,须要注意下double
的原子性逻辑,这里是官方文档的具体说明Non-Atomic Treatment of double
and long
,引用解释一下:
For the purposes of the Java programming language memory model, a single write to a non-volatile
long
ordouble
value is treated as two separate writes: one to each 32-bit half. This can result in a situation where a thread sees the first 32 bits of a 64-bit value from one write, and the second 32 bits from another write.Writes and reads of volatile
long
anddouble
values are always atomic.Writes to and reads of references are always atomic, regardless of whether they are implemented as 32-bit or 64-bit values.
Some implementations may find it convenient to divide a single write action on a 64-bit
long
ordouble
value into two write actions on adjacent 32-bit values. For efficiency's sake, this behavior is implementation-specific; an implementation of the Java Virtual Machine is free to perform writes tolong
anddouble
values atomically or in two parts.Implementations of the Java Virtual Machine are encouraged to avoid splitting 64-bit values where possible. Programmers are encouraged to declare shared 64-bit values as
volatile
or synchronize their programs correctly to avoid possible complications.
出于Java编程语言内存模型的缘由,对于没有volatile
修饰的long
或者double
的单个写操做,会被分红两次写操做,每次对32位操做。所以可能会致使线程会读到来自不一样线程写入的32位数据组合的错误结果。
对于volatile
修饰的long
和double
而言,写和读操做都是原子的。
对于引用的读写,无论是32位或者64的数据值,都是原子操做。
一些实现方案中也许会发现将64位数据的单次写操做分红两次相邻32位数据的写操做很方便。出于效率的缘故,这种是比较特殊的实现;JVM的实现能够自由的选择对long
和value
的写入采用原子逻辑或者分红两步。
鼓励JVM的实如今可能的状况下避免拆分64位的逻辑。对于程序员而言,鼓励在共享的64位值上添加volatile
或者synchronize
的声明修饰,避免复杂问题的出现。
从上面的描述能够看出来,原子性的问题是可能存在的。不过对于如今绝大部分的64位的机器以及使用64位的JVM时,这个问题通常是忽略的。可是当你使用的环境不符合要求时,请注意这个问题的存在。
总的代码逻辑来看,Double
和Float
的逻辑基本一致,由于都是IEEE 754
标准的浮点数,主要仍是使用的bit数不一样带来的一些差距。若是你已经了解了float
,那再理解这个其实很简单。