前言
JAVA數(shù)值分整數(shù)和浮點(diǎn)數(shù),前一篇文章已經(jīng)解析了Java整數(shù)的存儲(chǔ)原理,本篇將接著解析浮點(diǎn)數(shù)在計(jì)算機(jī)里的存儲(chǔ)原理。Java浮點(diǎn)數(shù)分單精度類型( float)和雙精度類型(double),float 數(shù)據(jù)占用 32bit,double 數(shù)據(jù)占用 64bit。
Java浮點(diǎn)數(shù)標(biāo)準(zhǔn)
java中浮點(diǎn)數(shù)采用的IEEE754標(biāo)準(zhǔn),該標(biāo)準(zhǔn)的全稱為IEEE二進(jìn)制浮點(diǎn)數(shù)算術(shù)標(biāo)準(zhǔn)。這個(gè)標(biāo)準(zhǔn)規(guī)定的存儲(chǔ)格式是這樣的:符號(hào)位+指數(shù)位偏移+尾數(shù)位
IEEE 754常用的兩種表示浮點(diǎn)數(shù)值的方式:?jiǎn)尉_度(float 32位)、雙精確度(double 64位)
- 浮點(diǎn)數(shù)表示的數(shù)值:V = (-1)^s × M × 2^E
- 符號(hào)(sign) :1個(gè)bit表示,當(dāng)s=0,V為正數(shù);當(dāng)s=1,V為負(fù)數(shù)。
- 階碼(exponent) :E的作用是對(duì)浮點(diǎn)數(shù)加權(quán),用于存儲(chǔ)科學(xué)計(jì)數(shù)法中的指數(shù)數(shù)據(jù),并且采用移位存儲(chǔ)。float類型的階碼是 8 bits,double類型的階碼是 11 bits。
- 尾數(shù)(significand) :M是一個(gè)二進(jìn)制小數(shù),因?yàn)槭嵌M(jìn)制,所以科學(xué)計(jì)數(shù)法中這個(gè)值范圍是:1≤M<2。(和十進(jìn)制中范圍為1~10一樣)
IEEE 754對(duì)有效數(shù)字M和指數(shù)E,還有一些特別規(guī)定。前面說(shuō)過(guò),1≤M<2,也就是說(shuō),M可以寫(xiě)成1.xxxxxx的形式,其中xxxxxx表示小數(shù)部分。IEEE 754規(guī)定,在計(jì)算機(jī)內(nèi)部保存 M時(shí),默認(rèn)這個(gè)數(shù)的第一位總是1,因此可以被舍去,只保存后面的xxxxxx部分。比如保存1.01的時(shí)候,只保存01,等到讀取的時(shí)候,再把第一位的1加上去。這樣做的目的,是節(jié)省1位有效數(shù)字。以32位float浮點(diǎn)數(shù)為例,留給M只有23位,將第一位的1舍去以后,等于可以保存24位有效數(shù)字。 道理就是在這里,那 24bit 能精確到小數(shù)點(diǎn)后幾位呢,我們知道 9 的二進(jìn)制表示為 1001,所以 4bit 能精確十進(jìn)制中的 1 位小數(shù)點(diǎn), 24bit 就能使 float 能精確到小數(shù)點(diǎn)后 6 位。
至于指數(shù)E,情況就比較復(fù)雜。 首先,E為一個(gè)無(wú)符號(hào)整數(shù)(unsigned int)這意味著,如果E為8位 (float類型) ,它的取值范圍為0~255;如果E為11位(double類型),它的取值范圍 為0~2047。但是,我們知道,科學(xué)計(jì)數(shù)法中的E是可以出現(xiàn)負(fù)數(shù)的(因?yàn)?.75用科學(xué)計(jì)數(shù)法表示就是1.1*2^-1),所以 IEEE 754規(guī)定,存入內(nèi)存時(shí)E的真實(shí)值必須再加上一個(gè)中間數(shù),對(duì)于8位的E,這個(gè)中間數(shù)是127;對(duì)于11位的E,這個(gè)中間數(shù)是1023。比如,2^10的E是10,所以保存成32位浮點(diǎn)數(shù)時(shí),必須保存成10+127=137,即10001001。
浮點(diǎn)數(shù)轉(zhuǎn)二進(jìn)制數(shù)
能精確表示的浮點(diǎn)數(shù)
哪些小數(shù)能被精確表示呢?0.5的倍數(shù),且在精度以內(nèi)。
方便計(jì)算,首先選擇可以用浮點(diǎn)數(shù)精確表示的數(shù)計(jì)算:4.25
- step1.首先將數(shù)字轉(zhuǎn)為2進(jìn)制:
整數(shù)部分4: "除2取余"
4/2=2 余 0
2/2=1 余 0
1/2=0 余 1
小數(shù)部分0.25:"乘2取整"
0.25 * 2 = 0.5 未進(jìn)位 0
0.50 * 2 = 1 進(jìn)位整數(shù) 1
二進(jìn)制表示:100.01
- step2.將二進(jìn)制數(shù)轉(zhuǎn)為科學(xué)計(jì)數(shù)法表示
科學(xué)記數(shù)法表示:1.0001 * 2^2
- step3.轉(zhuǎn)換為IEEE754格式存儲(chǔ)
符號(hào)位 0 (正數(shù)0 負(fù)數(shù)1)
指數(shù) 2 (float指數(shù)+127=127 double指數(shù)+1023=1025)
尾數(shù) 0001
單精度f(wàn)loat:符號(hào)位0 指數(shù)位129(10000001) 尾數(shù)001
0 10000001 00010000000000000000000
雙精度double:符號(hào)位0 指數(shù)位1025(10000000001) 尾數(shù)001
0 10000000001 0001000000000000000000000000000000000000000000000000
不能精確表示的浮點(diǎn)數(shù)
舉個(gè)例子:1/3,十進(jìn)制就無(wú)法精確表示三分之一這個(gè)數(shù)字。
二進(jìn)制也有很多很多小數(shù)無(wú)法精確表示,包括:0.1和0.2,這也是導(dǎo)致計(jì)算出現(xiàn)精度問(wèn)題的根本原因。
下面將0.1轉(zhuǎn)為2進(jìn)制表示。
0.1
- step1.首先將數(shù)字轉(zhuǎn)為2進(jìn)制:
0.10 * 2 = 0.20 未進(jìn)位 0
0.20 * 2 = 0.40 未進(jìn)位 0
0.40 * 2 = 0.80 未進(jìn)位 0
0.80 * 2 = 1.60 進(jìn)位 1
0.60 * 2 = 1.20 進(jìn)位 1
0.20 * 2 = 0.40 未進(jìn)位 0
0.40 * 2 = 0.80 未進(jìn)位 0
0.80 * 2 = 1.60 進(jìn)位 1
0.60 * 2 = 1.20 進(jìn)位 1
無(wú)限循環(huán)(0011)...
二進(jìn)制表示0.1:
0.00011001100110011001100110011001100110011001100110011001...
- step2.將二進(jìn)制數(shù)轉(zhuǎn)為科學(xué)計(jì)數(shù)法表示
科學(xué)記數(shù)表示:
1.1001100110011001100110011001100110011001100110011001... * 2^-4
- step3.轉(zhuǎn)換為IEEE754格式存儲(chǔ)
符號(hào)位 0 (正數(shù)0 負(fù)數(shù)1)
指數(shù) -4 (float指數(shù)+127 double指數(shù)+1023)
尾數(shù) 1001100110011001100110011001100110011001100110011001...
float 單精度浮點(diǎn)數(shù),尾數(shù)只能存儲(chǔ)23位,多余位數(shù)四舍五入:
0 01111011 10011001100110011001101
double 雙精度浮點(diǎn)數(shù),尾數(shù)只能存儲(chǔ)52位,多余位數(shù)四舍五入:
0 01111111011 1001100110011001100110011001100110011001100110011010
結(jié)語(yǔ)
本文詳細(xì)解讀了Java浮點(diǎn)數(shù)(包括單精度和雙精度)在計(jì)算機(jī)中的存儲(chǔ)原理,并且采用示例講解了浮點(diǎn)數(shù)轉(zhuǎn)換成二進(jìn)制的方法,希望大家看完本篇后能夠弄清楚浮點(diǎn)數(shù)是怎么在計(jì)算機(jī)中存儲(chǔ)的。