在看Float前須要先了解IEEE 754標準,該標準定義了浮點數的格式還有一些特殊值,它規定了計算機中二進制與十進制浮點數轉換的格式及方法。規定了四種表示浮點數值的方法,單精確度(32位)、雙精確度(64位)、延伸單精確度(43位以上)與延伸雙精確度(79位以上)。多數編程語言支持單精確度和雙精確度,這裏討論的Float就是Java的單精確度的實現。html
浮點數由三部分組成,以下圖,符號位s、指數e和尾數f。java
對於求值咱們是有一個公式對應的,根據該公式來看會更簡單點,某個浮點數的值爲:編程
能夠看到32位的最高位爲符號標識符,1表示負數,0表示正數。指數部分爲8位,其實能夠是0到255,可是爲了可正可負,這裏須要減去127後纔是真正的指數,而底數固定爲2。剩下的23位表示尾數,但默認前面都會加上1.。因此經過上面就能夠將一個浮點數表示出來了。數組
咱們舉個例子來看,二進制的「01000001001101100000000000000000」表示的浮點數是啥?bash
Java的Float類主要的做用就是對基本類型float進行封裝,提供了一些處理float類型的方法,好比float到String類型的轉換方法或String類型到float類型的轉換方法,固然也包含與其餘類型之間的轉換方法。併發
--java.lang.Object
--java.lang.Number
--java.lang.Float複製代碼
public static final float POSITIVE_INFINITY = 1.0f / 0.0f;
public static final float NEGATIVE_INFINITY = -1.0f / 0.0f;
public static final float NaN = 0.0f / 0.0f;
public static final float MAX_VALUE = 0x1.fffffeP+127f;
public static final float MIN_NORMAL = 0x1.0p-126f;
public static final float MIN_VALUE = 0x0.000002P-126f;
public static final int MAX_EXPONENT = 127;
public static final int MIN_EXPONENT = -126;
public static final int SIZE = 32;
public static final int BYTES = SIZE / Byte.SIZE;
public static final Class<Float> TYPE = (Class<Float>) Class.getPrimitiveClass("float");複製代碼
POSITIVE_INFINITY
用來表示正無窮大,按照IEEE 754浮點標準規定,任何有限正數除以0爲正無窮大,正無窮的值爲0x7f800000。NEGATIVE_INFINITY
用來表示負無窮大,任何有限負數除以0爲負無窮的,負無窮的值爲0xff800000。NaN
用來表示處理計算中出現的錯誤狀況,好比0除以0或負數平方根。對於單精度浮點數,IEEE 標準規定 NaN 的指數域全爲 1,且尾數域不等於零的浮點數。它並無要求具體的尾數域,因此 NaN 實際上不非是一個,而是一族。Java這裏定義的值爲0x7fc00000。MAX_VALUE
用來表示最大的浮點數值,它定義爲0x1.fffffeP+127f,這裏0x表示十六進制,1.fffffe表示十六進制的小數,P表示2,+表示幾回方,這裏就是2的127次方,最後的f是轉成浮點型。因此最後最大值爲3.4028235E38。MIN_NORMAL
用來表示最小標準值,它定義爲0x1.0p-126f,這裏其實就是2的-126次方的了,值爲1.17549435E-38f。MIN_VALUE
用來表示浮點數最小值,它定義爲0x0.000002P-126f,最後的值爲1.4e-45f。MAX_EXPONENT
用來表示指數的最大值,這裏定爲127,這個也是按照IEEE 754浮點標準的規定。MIN_EXPONENT
用來表示指數的最小值,按照IEEE 754浮點標準的規定,它爲-126。SIZE
用來表示二進制float值的比特數,值爲32,靜態變量且不可變。BYTES
用來表示二進制float值的字節數,值爲SIZE
除於Byte.SIZE
,結果爲4。TYPE
的toString的值是float
。getPrimitiveClass
是一個native方法,在Class.c
中有個Java_java_lang_Class_getPrimitiveClass
方法與之對應,因此JVM層面會經過JVM_FindPrimitiveClass
函數根據"float"字符串得到jclass,最終到Java層則爲Class<Float>
。JNIEXPORT jclass JNICALL
Java_java_lang_Class_getPrimitiveClass(JNIEnv *env,
jclass cls,
jstring name)
{
const char *utfName;
jclass result;
if (name == NULL) {
JNU_ThrowNullPointerException(env, 0);
return NULL;
}
utfName = (*env)->GetStringUTFChars(env, name, 0);
if (utfName == 0)
return NULL;
result = JVM_FindPrimitiveClass(env, utfName);
(*env)->ReleaseStringUTFChars(env, name, utfName);
return result;
}複製代碼
當TYPE
執行toString時,邏輯以下,則實際上是getName
函數決定其值,getName
經過native方法getName0
從JVM層獲取名稱,編程語言
public String toString() {
return (isInterface() ? "interface " : (isPrimitive() ? "" : "class "))
+ getName();
}複製代碼
getName0
根據一個數組得到對應的名稱,JVM根據Java層的Class可獲得對應類型的數組下標,好比這裏下標爲6,則名稱爲"float"。函數
const char* type2name_tab[T_CONFLICT+1] = {
NULL, NULL, NULL, NULL,
"boolean",
"char",
"float",
"double",
"byte",
"short",
"int",
"long",
"object",
"array",
"void",
"*address*",
"*narrowoop*",
"*conflict*"
};複製代碼
public static float parseFloat(String s) throws NumberFormatException {
return FloatingDecimal.parseFloat(s);
}複製代碼
經過調用FloatingDecimal的parseFloat方法來實現對字符串的轉換,FloatingDecimal類主要提供了對 IEEE-754,該方法的實現代碼實在是太長,這裏再也不貼出了,說下它的處理思想及步驟。oop
$(-1)^s*(1.f)*2^{(e-127)}$
轉換的,能夠看到它的精度由尾數來決定,尾數有23位,那麼$2^{23}=8388608$
,該值介於$10^{6}$
到$10^{7}$
,因此它能保證6位精確的數,可是7位就不必定了,這裏是相對小數點來講的,因此對應整個浮點型的精確值爲有效位數就是7位,8位的不必定能準確表示。這裏對比幾個例子,字符串30.200019轉換後爲30.20002,一共七位有效位;字符串30.200001轉換後爲30.2,一共七位有效位,但後面都爲0,因此省略;字符串30000.2196501轉換後爲30000.219,一共八位有效位,恰好能準確表示八位。public Float(String s) throws NumberFormatException {
value = parseFloat(s);
}
public Float(float value) {
this.value = value;
}
public Float(double value) {
this.value = (float)value;
}複製代碼
提供三種構造函數,都比較簡單,可傳入String、float和double類型值,其中String類型會調用parseFloat方法進行轉換,double則直接轉成float類型。優化
public String toString() {
return Float.toString(value);
}
public static String toString(float f) {
return FloatingDecimal.toJavaFormatString(f);
}複製代碼
兩個toString方法,主要看第二個,經過FloatingDecimal類的toJavaFormatString方法轉成字符串。這個轉換過程也是比較複雜,這裏再也不貼代碼,它處理的過程是先將浮點數轉成IEEE-754標準的二進制形式,而且還要判斷是不是正負無窮大,是不是NaN。而後再按照IEEE-754標準從二進制轉換成十進制,此過程十分複雜,須要考慮的點至關多。最後生成浮點數對應的字符串。
public static Float valueOf(float f) {
return new Float(f);
}
public static Float valueOf(String s) throws NumberFormatException {
return new Float(parseFloat(s));
}複製代碼
有兩個valueOf方法,對於float型的直接new一個Float對象返回,而對於字符串則先調用parseFloat方法轉成float後再new一個Float對象返回。
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 value;
}
public double doubleValue() {
return (double)value;
}複製代碼
包括byteValue、shortValue、intValue、longValue、floatValue和doubleValue等方法,其實就是轉換成對應的類型。
public static native int floatToRawIntBits(float value);
JNIEXPORT jint JNICALL
Java_java_lang_Float_floatToRawIntBits(JNIEnv *env, jclass unused, jfloat v)
{
union {
int i;
float f;
} u;
u.f = (float)v;
return (jint)u.i;
}複製代碼
floatToRawIntBits是一個本地方法,該方法主要是將一個浮點數轉成IEEE 754標準的二進制形式對應的整型數。對應的本地方法的處理邏輯簡單並且有效,就是經過一個union實現了int和float的轉換,最後再轉成java的整型jint。
public static native float intBitsToFloat(int bits);
JNIEXPORT jfloat JNICALL
Java_java_lang_Float_intBitsToFloat(JNIEnv *env, jclass unused, jint v)
{
union {
int i;
float f;
} u;
u.i = (long)v;
return (jfloat)u.f;
}複製代碼
該方法與floatToRawIntBits方法對應,floatToIntBits一樣是一個本地方法,該方法主要是將一個IEEE 754標準的二進制形式對應的整型數轉成一個浮點數。能夠看到其本地實現也是經過union來實現的,完成int轉成float,最後再轉成java的浮點型jfloat。
public static int floatToIntBits(float value) {
int result = floatToRawIntBits(value);
if ( ((result & FloatConsts.EXP_BIT_MASK) ==
FloatConsts.EXP_BIT_MASK) &&
(result & FloatConsts.SIGNIF_BIT_MASK) != 0)
result = 0x7fc00000;
return result;
}複製代碼
該方法主要先經過調用floatToRawIntBits獲取到IEEE 754標準對應的整型數,而後再分別用FloatConsts.EXP_BIT_MASK和FloatConsts.SIGNIF_BIT_MASK兩個掩碼去判斷是否爲NaN,0x7fc00000對應的即爲NaN。
public int hashCode() {
return Float.hashCode(value);
}
public static int hashCode(float value) {
return floatToIntBits(value);
}複製代碼
主要看第二個hashCode方法便可,它是經過調用floatToIntBits來實現的,因此它返回的哈希碼其實就是某個浮點數的IEEE 754標準對應的整型數。
public static boolean isFinite(float f) {
return Math.abs(f) <= FloatConsts.MAX_VALUE;
}
public static boolean isInfinite(float v) {
return (v == POSITIVE_INFINITY) || (v == NEGATIVE_INFINITY);
}複製代碼
這兩個方法分別用於判斷一個浮點數是否爲有窮數或無窮數。邏輯很簡單,絕對值小於FloatConsts.MAX_VALUE的數則爲有窮數,FloatConsts.MAX_VALUE的值爲3.4028235e+38f,它其實與前面Float類中定義的MAX_VALUE相同。而是否爲無窮數則經過POSITIVE_INFINITY和NEGATIVE_INFINITY進行判斷。
public static boolean isNaN(float v) {
return (v != v);
}複製代碼
用於判斷是個浮點數是否爲NaN,該方法邏輯很簡單,直接(v != v),爲啥能這樣作?由於規定一個NaN與任何值都不相等,包括它本身。因此這部分邏輯在JVM或本地中會作,因而能夠直接經過比較來判斷。
public static float max(float a, float b) {
return Math.max(a, b);
}
public static float min(float a, float b) {
return Math.min(a, b);
}複製代碼
用於獲取二者較大或較小值,直接交由Math類完成。
public static int compare(float f1, float f2) {
if (f1 < f2)
return -1;
if (f1 > f2)
return 1;
int thisBits = Float.floatToIntBits(f1);
int anotherBits = Float.floatToIntBits(f2);
return (thisBits == anotherBits ? 0 :
(thisBits < anotherBits ? -1 :
1));
}複製代碼
f1小於f2則返回-1,反之則返回1。沒法經過上述直接比較時則使用floatToIntBits方法分別將f1和f2轉成IEEE 754標準對應的整型數,而後再比較。相等則返回0,不然返回-1或1。
如下是廣告和相關閱讀
========廣告時間========
鄙人的新書《Tomcat內核設計剖析》已經在京東銷售了,有須要的朋友能夠到 item.jd.com/12185360.ht… 進行預約。感謝各位朋友。
=========================
相關閱讀:
從JDK源碼角度看Object
從JDK源碼角度看Long
從JDK源碼角度看Integer
volatile足以保證數據同步嗎
談談Java基礎數據類型
從JDK源碼角度看併發鎖的優化
從JDK源碼角度看線程的阻塞和喚醒
從JDK源碼角度看併發競爭的超時
從JDK源碼角度看java併發線程的中斷
從JDK源碼角度看Java併發的公平性
從JDK源碼角度看java併發的原子性如何保證
從JDK源碼角度看Byte
從JDK源碼角度看Boolean
從JDK源碼角度看Short
有幫助,可打賞:
歡迎關注: