此次來看看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
,那再理解這個其實很簡單。