[Java源碼]Double

此次來看看Double的源代碼,基於 jdk1.8.0_181.jdk 版本,若有錯誤,歡迎聯繫指出。html

前言

Doubledouble基礎數據類型的包裝類,而doubleIEEE 754標準的雙精度 64bit 的浮點數,具體IEEE 754標準的一些信息這裏就再也不詳細的介紹了,建議能夠看看個人上一篇文章 [Java源碼]Float 對此有個大體的瞭解,二者的邏輯基本是一致的。Java中對於十進制浮點數,double是默認的類型。java

雙精度(64 bit)

繼續使用相似的圖說明下, 程序員

  • sign: 符號位,1位。0表示正數,1表示負數。
  • exponent: 指數位,11位。雙精度的指數部分是−1022~+1024加上偏移值1023,指數值的大小從1~2046(0和2047是特殊值)
  • fraction: 尾數位,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,尾數域全爲0
  • NEGATIVE_INFINITY 表示負無窮,值爲0xfff0000000000000L標準定義指數域全爲1,尾數域全爲0
  • NaN 英文縮寫,Not-a-Number標準定義爲 指數域全爲1,尾數域不全爲0
public 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 表示了最大的指數值,爲1023
  • MIN_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;
複製代碼

Doubledouble的包裝類,這裏存放了對應的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

parseDouble 方法

public static double parseDouble(String s) throws NumberFormatException {
  return FloatingDecimal.parseDouble(s);
}
複製代碼

內部調用了FloatingDecimal.parseDouble實現具體邏輯,其中具體的處理過程和Float相似,能夠查看 parsefloat 方法 瞭解,這裏就再也不重複敘述了。結果返回對應的double類型數據值。post

toString 方法

public static String toString(double d) {
  return FloatingDecimal.toJavaFormatString(d);
}
複製代碼

依然是調用了FloatingDecimal.toJavaFormatString的方法,處理過程和Float也基本一致,結果返回對應的字符串格式。

toHexString 方法

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();
  }
}
複製代碼

總體的邏輯在代碼註釋中進行了說明,清晰且簡單,結果返回對應的十六進制字符串。

valueOf 方法

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一個新對象返回。

isNaN 方法

public static boolean isNaN(double v) {
  return (v != v);
}

public boolean isNaN() {
  return isNaN(value);
}
複製代碼

判斷是不是NaN,使用(v != v)判斷;具體NaN的規則描述能夠參考 isNaN 方法

isInfinite 方法

public static boolean isInfinite(double v) {
  return (v == POSITIVE_INFINITY) || (v == NEGATIVE_INFINITY);
}

public boolean isInfinite() {
  return isInfinite(value);
}
複製代碼

判斷是否是無窮數,包含正無窮和負無窮

isFinite 方法

public static boolean isFinite(double d) {
  return Math.abs(d) <= DoubleConsts.MAX_VALUE;
}
複製代碼

經過輸入參數絕對值是否小於double類型的最大值,判斷是否是有限數

xxxValue 方法

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;
}
複製代碼

返回對應類型的值,直接進行強制類型轉換

hashCode 方法

@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

longBitsToDouble 方法

public static native double longBitsToDouble(long bits);
複製代碼

longBitsToDouble是個native方法,由c代碼實現。返回對應double數據值

  • 參數爲0x7ff0000000000000L時,結果爲正無窮

  • 參數爲0xfff0000000000000L時,結果爲負無窮

  • 參數在0x7ff0000000000001L ~ 0x7fffffffffffffffL或者0xfff0000000000001L ~ 0xffffffffffffffffL之間時,結果爲NaN

doubleToRawLongBits 方法

public static native long doubleToRawLongBits(double value);
複製代碼

doubleToRawLongBits是個native方法,由對應的c代碼實現。

結果會保留NaN值,正無窮結果爲0x7ff0000000000000L;負無窮結果爲0xfff0000000000000L;當參數爲NaN時,結果會是輸入參數對應的實際整數值,該方法不會像doubleToLongBits,對NaN進行統一的返回值處理

doubleToLongBits 方法

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 方法 說明。

equals 方法

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
複製代碼

compare 方法

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.00.0Double.NaN-Double.NaN這類的特殊值,能夠自行編寫幾個進行測試一下。

sum、min、max方法

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.00.0Double.NaN-Double.NaN這類的特殊值,能夠自行測試下結果,也許會出乎你的意料哦。

特別說明

由於double是64bit,須要注意下double的原子性邏輯,這裏是官方文檔的具體說明Non-Atomic Treatment of doubleand long,引用解釋一下:

For the purposes of the Java programming language memory model, a single write to a non-volatile longordoublevalue 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 volatilelongand doublevalues 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 longor doublevalue 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 to longand doublevalues 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 volatileor synchronize their programs correctly to avoid possible complications.

出於Java編程語言內存模型的緣由,對於沒有volatile修飾的long或者double的單個寫操做,會被分紅兩次寫操做,每次對32位操做。所以可能會致使線程會讀到來自不一樣線程寫入的32位數據組合的錯誤結果。

對於volatile修飾的longdouble而言,寫和讀操做都是原子的。

對於引用的讀寫,無論是32位或者64的數據值,都是原子操做。

一些實現方案中也許會發現將64位數據的單次寫操做分紅兩次相鄰32位數據的寫操做很方便。出於效率的緣故,這種是比較特殊的實現;JVM的實現能夠自由的選擇對longvalue的寫入採用原子邏輯或者分紅兩步。

鼓勵JVM的實如今可能的狀況下避免拆分64位的邏輯。對於程序員而言,鼓勵在共享的64位值上添加volatile或者synchronize的聲明修飾,避免複雜問題的出現。

從上面的描述能夠看出來,原子性的問題是可能存在的。不過對於如今絕大部分的64位的機器以及使用64位的JVM時,這個問題通常是忽略的。可是當你使用的環境不符合要求時,請注意這個問題的存在

總結

總的代碼邏輯來看,DoubleFloat的邏輯基本一致,由於都是IEEE 754標準的浮點數,主要仍是使用的bit數不一樣帶來的一些差距。若是你已經瞭解了float,那再理解這個其實很簡單。

最後

博客原文地址:blog.renyijiu.com/post/java源碼…

相關文章
相關標籤/搜索