當(dāng)一個同事問我:Java中的double的取值范圍是多少時,我一臉的茫然,除了知道浮點(diǎn)數(shù)由符號位、指數(shù)位和小數(shù)位組成之外,其它的一無所知。大學(xué)里《計(jì)算機(jī)組成》中學(xué)的東西也忘得一干二凈。
查了一些資料,并親手寫了些測試代碼,總算弄明白了,在此做個筆記。
查了一些資料,并親手寫了些測試代碼,總算弄明白了,在此做個筆記。
1.三種存儲格式
Java遵循的是IEEE 754 規(guī)范。在這個規(guī)范里,提到了浮點(diǎn)數(shù)的三種類型:單精度、雙精度和雙精度擴(kuò)展。
這三種類型的浮點(diǎn)數(shù)的存儲都由三部分組成:符號位、指數(shù)位和小數(shù)位組成,不同的是三者指數(shù)位和小數(shù)位的位數(shù)不一樣。
IEEE 單精度格式具有24 位有效數(shù)字精度,并總共占用32 位。IEEE 雙精度格式具有53 位有效數(shù)字精度,并總共占用64 位。至于雙精度擴(kuò)展,IEEE規(guī)定它至少具有64 位有效數(shù)字精度,并總共占用至少79 位。
這三種類型的浮點(diǎn)數(shù)的存儲都由三部分組成:符號位、指數(shù)位和小數(shù)位組成,不同的是三者指數(shù)位和小數(shù)位的位數(shù)不一樣。
IEEE 單精度格式具有24 位有效數(shù)字精度,并總共占用32 位。IEEE 雙精度格式具有53 位有效數(shù)字精度,并總共占用64 位。至于雙精度擴(kuò)展,IEEE規(guī)定它至少具有64 位有效數(shù)字精度,并總共占用至少79 位。
2.雙精度格式
現(xiàn)在,我們僅僅對雙精度浮點(diǎn)數(shù),也就是double進(jìn)行分析,其它的兩種可以此類推,不必贅述。
IEEE 雙精度格式由三部分組成:52位小數(shù)f ;11 位偏置指數(shù)e ;以及1 位符號s。
這些字段連續(xù)存儲在兩個32 位字中。如下圖所示:
IEEE 雙精度格式由三部分組成:52位小數(shù)f ;11 位偏置指數(shù)e ;以及1 位符號s。
這些字段連續(xù)存儲在兩個32 位字中。如下圖所示:
將這兩個連續(xù)的32 位字按一個64 位字那樣進(jìn)行了編號,其中0:51 位存儲52 位的小數(shù)f ; 52:62 位存儲11 位偏置指數(shù)e ;而第63 位存儲符號位s。
s為0表示整數(shù),1則為負(fù)數(shù)。
e[52:62]總共11位表示偏置指數(shù),也就是階碼部分。它是一個無符號數(shù),取值范圍是[0,2 11 -1],也就是[0,2047]。當(dāng)0<e<2047時,它表示的指數(shù)值為e-1023;當(dāng)它為0時,表示的指數(shù)值為-1022;而當(dāng)e=2047時,它表示無窮大或無意義的數(shù)(這個稍后再討論)。
現(xiàn)在,就剩下最后的小數(shù)部分了,也就是尾數(shù)。
小數(shù)分為規(guī)格化數(shù)和非規(guī)格化數(shù)。規(guī)格化數(shù)的小數(shù)點(diǎn)左邊隱含了1,而非規(guī)格化數(shù)小數(shù)點(diǎn)左邊是0。小數(shù)點(diǎn)左邊隱含1有什么好處呢?我們知道,任何一個小數(shù)都可以用科學(xué)計(jì)數(shù)法表示:一個數(shù)可以表示成 a×10 n 的 形式,其中1≤a<10,n為整數(shù)。在十進(jìn)制里,a的整數(shù)部分必定是1-9的整數(shù)。而在二進(jìn)制里,a的整數(shù)部分就只能是1了。既然必定是1,那么為這個常 量浪費(fèi)1位存儲空間顯然不劃算了,不如省掉,因此1就隱含了。這也就是為什么0<e<2047時,小數(shù)部分使用規(guī)格化數(shù)的原因。
那么為什么當(dāng)e=0時,它的小數(shù)部分又是非規(guī)格化數(shù)呢?原因很簡單,如果此時也用規(guī)格化數(shù),那么它的取值范圍必定會出現(xiàn)斷層。也就是說,在最大值和最小值之間,還有部分?jǐn)?shù)字取值范圍的數(shù)字無法表示。
上面,我們提到了當(dāng)e=2047時,它表示無窮大或無意義的數(shù)。如果此時,小數(shù)部分全0,它就是無窮大,至于是正無窮大還是負(fù)無窮大由符號位決定,如果小數(shù)部分至少有1位不為0,那么它就是無意義的數(shù)。
用圖表表示如下:
s為0表示整數(shù),1則為負(fù)數(shù)。
e[52:62]總共11位表示偏置指數(shù),也就是階碼部分。它是一個無符號數(shù),取值范圍是[0,2 11 -1],也就是[0,2047]。當(dāng)0<e<2047時,它表示的指數(shù)值為e-1023;當(dāng)它為0時,表示的指數(shù)值為-1022;而當(dāng)e=2047時,它表示無窮大或無意義的數(shù)(這個稍后再討論)。
現(xiàn)在,就剩下最后的小數(shù)部分了,也就是尾數(shù)。
小數(shù)分為規(guī)格化數(shù)和非規(guī)格化數(shù)。規(guī)格化數(shù)的小數(shù)點(diǎn)左邊隱含了1,而非規(guī)格化數(shù)小數(shù)點(diǎn)左邊是0。小數(shù)點(diǎn)左邊隱含1有什么好處呢?我們知道,任何一個小數(shù)都可以用科學(xué)計(jì)數(shù)法表示:一個數(shù)可以表示成 a×10 n 的 形式,其中1≤a<10,n為整數(shù)。在十進(jìn)制里,a的整數(shù)部分必定是1-9的整數(shù)。而在二進(jìn)制里,a的整數(shù)部分就只能是1了。既然必定是1,那么為這個常 量浪費(fèi)1位存儲空間顯然不劃算了,不如省掉,因此1就隱含了。這也就是為什么0<e<2047時,小數(shù)部分使用規(guī)格化數(shù)的原因。
那么為什么當(dāng)e=0時,它的小數(shù)部分又是非規(guī)格化數(shù)呢?原因很簡單,如果此時也用規(guī)格化數(shù),那么它的取值范圍必定會出現(xiàn)斷層。也就是說,在最大值和最小值之間,還有部分?jǐn)?shù)字取值范圍的數(shù)字無法表示。
上面,我們提到了當(dāng)e=2047時,它表示無窮大或無意義的數(shù)。如果此時,小數(shù)部分全0,它就是無窮大,至于是正無窮大還是負(fù)無窮大由符號位決定,如果小數(shù)部分至少有1位不為0,那么它就是無意義的數(shù)。
用圖表表示如下:
雙精度存儲格式位模式及其IEEE 值的位模式的對應(yīng)關(guān)系可參見下表:
上圖中的無意義數(shù)(NaN,非數(shù))的位模式只是可表示NaN的眾多位模式中的一種而已。另外,我們注意到,盡管+0和-0的十進(jìn)制值是相等的,但它們的位模式卻不一樣。
3.代碼示例
既然對存儲格式已經(jīng)了解清楚了,我們可以通過編寫代碼來加深對浮點(diǎn)數(shù)存儲的理解。
Java中的類Double封裝了 double的操作,我們很容易通過Double來操作double. 函數(shù)Double.longBitsToDouble()可以把給定的位模式轉(zhuǎn)換成double。如果要驗(yàn)證十六進(jìn)制的 0x7fefffffffffffff位模式是不是十進(jìn)制的,這個很容易,只需要如下兩行代碼:
Java中的類Double封裝了 double的操作,我們很容易通過Double來操作double. 函數(shù)Double.longBitsToDouble()可以把給定的位模式轉(zhuǎn)換成double。如果要驗(yàn)證十六進(jìn)制的 0x7fefffffffffffff位模式是不是十進(jìn)制的,這個很容易,只需要如下兩行代碼:


我們可以看到,輸出結(jié)果是:
1.7976931348623157E308
這與我們期望中的也是吻合的。
其它的例子我就不逐一演示,這兒我附上代碼的鏈接。
〈下載〉
參考資料:
1.IEEE Standard 754 for Binary Floating-Point Arithmetic
2.Numerical Computation Guide( http://gceclub.sun.com.cn/TT/sunstudio/NCG/819-4817-10.pdf )
1.IEEE Standard 754 for Binary Floating-Point Arithmetic
2.Numerical Computation Guide( http://gceclub.sun.com.cn/TT/sunstudio/NCG/819-4817-10.pdf )
(完)
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯(lián)系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長非常感激您!手機(jī)微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
