??本文又臭又長(zhǎng)恕沫,實(shí)用價(jià)值不大养晋。只是小碼農(nóng)最近腦子抽風(fēng)突然想搞明白為什么0.1+0.2=0.30000000000000004才查閱資料寫的殷蛇。嫌浪費(fèi)時(shí)間者可以不看...
??在說這個(gè)問題之前倔监,我們先看兩個(gè)常識(shí)批旺。第一個(gè)是n位二進(jìn)制能最大表示的無(wú)符號(hào)整數(shù)销睁?這個(gè)大家應(yīng)該都知道是20 + 21 + … + 2n-1供璧,即2n – 1。第二個(gè)是二進(jìn)制的科學(xué)計(jì)數(shù)法(這是為了之后好分析冻记,小碼農(nóng)自創(chuàng)的一種說法)睡毒。小學(xué)我們就知道十進(jìn)制中有一個(gè)科學(xué)計(jì)數(shù)法,即如825.25會(huì)被表示成8.2525 * 102冗栗。那么在二進(jìn)制的世界中也可以有一個(gè)科學(xué)計(jì)數(shù)法演顾。如8.25在二進(jìn)制被表示為1010.01供搀,寫成科學(xué)計(jì)數(shù)法就可以表示成1.01001 * 23。120.5在二進(jìn)制中為1110110.1钠至,寫成科學(xué)計(jì)數(shù)法就是1.1101101 * 26葛虐。所以是不是發(fā)現(xiàn)在二進(jìn)制的科學(xué)計(jì)數(shù)法中浮點(diǎn)數(shù)可以被表示成如1.xxx * 2t的形式呢。而事實(shí)上也如此棉钧,計(jì)算機(jī)內(nèi)部表示一個(gè)浮點(diǎn)數(shù)會(huì)將其分成三個(gè)部分:符號(hào)位(0為正屿脐、1位負(fù)。記為S)宪卿、尾數(shù)部分(1.xxx的诵。記xxx部分為M)和指數(shù)部分(2t。記t為E)佑钾。所以二進(jìn)制的科學(xué)計(jì)數(shù)法就是: (-1)S * (1.M) * 2E奢驯。
??明白這兩個(gè)概念之后我們就可以來(lái)解釋Java浮點(diǎn)數(shù)的表示范圍了。首先Java使用的浮點(diǎn)數(shù)運(yùn)算規(guī)則是IEEE 754次绘。那么什么是IEEE 754呢?簡(jiǎn)單的說就是一個(gè)美國(guó)定義的被廣泛采用的浮點(diǎn)數(shù)運(yùn)算標(biāo)準(zhǔn)撒遣。再按照上段的格式可得邮偎,在32位的float中分配規(guī)則為:1位符號(hào)位(S)、8位指數(shù)位(E)义黎、23位尾數(shù)位(M)禾进。在64位的double中為:1位符號(hào)位(S)、 11位指數(shù)位(E)廉涕、52位尾數(shù)位(M)泻云。
??我們剛才說到的二進(jìn)制的科學(xué)計(jì)數(shù)法為(-1)S * (1.M) * 2E。這個(gè)其實(shí)是不準(zhǔn)確的狐蜕,為什么不準(zhǔn)確呢宠纯?請(qǐng)繼續(xù)看下去。
??大家都知道在一串連續(xù)的二進(jìn)制位中想正數(shù)和負(fù)數(shù)都表示出來(lái)一般采用的方法是在最高位加一個(gè)符號(hào)位层释。但是這樣在運(yùn)算的時(shí)候就需要采用補(bǔ)碼進(jìn)行運(yùn)算婆瓜。比如在4bit的情況下-5的二進(jìn)制表示為1101,+4的二進(jìn)制表示為0100贡羔。直接使用原碼進(jìn)行相加后得到的值為10001廉白,竟然直接溢出了!換算成補(bǔ)碼后-5的補(bǔ)碼為1011乖寒,+4的補(bǔ)碼為0100猴蹂,運(yùn)算后再轉(zhuǎn)換為原碼可得1001,恰是-1楣嘁。即符號(hào)位會(huì)造成限制的磅轻。
??所以由以上所有解釋可得:n位二進(jìn)制位能表示的范圍為:[-2n-1 , 2n-1-1]珍逸,也就是說在E中還有一位符號(hào)位。但I(xiàn)EEE 754并沒有采用這種存儲(chǔ)方式瓢省,而是采用了無(wú)符號(hào)數(shù)值減去一個(gè)偏正值的形式來(lái)存儲(chǔ)整數(shù)弄息。即若指數(shù)部分存儲(chǔ)的為00000111,實(shí)際上表示的是:00000111 – 偏正值勤婚。偏正值為2n-1-1摹量。這種方式被稱為指數(shù)偏差。為什么呢馒胆?我們不妨考慮一個(gè)情況缨称,在需要對(duì)指數(shù)部分做比較時(shí),-5和-4做比較祝迂,符號(hào)位相同睦尽,其他位從高至低誰(shuí)先為0誰(shuí)較大,但+5和+4作比較型雳,符號(hào)位相同当凡,其他位從高至低誰(shuí)先為1誰(shuí)較大。這兩種比較方式恰是相反的纠俭,在硬件上更難實(shí)現(xiàn)沿量。既然這樣,那為什么還需要存在符號(hào)位呢冤荆,不如都使用這種指數(shù)偏差的形式朴则?小碼農(nóng)認(rèn)為這是由于IEEE 754僅規(guī)定了四種精度的浮點(diǎn)數(shù),偏正值都是相對(duì)固定的钓简。而對(duì)于任意n位二進(jìn)制位的數(shù)值來(lái)說乌妒,采用指數(shù)偏差的形式存儲(chǔ)每次都需要計(jì)算偏正值,時(shí)間效率上是難以接受的外邓。
??這個(gè)時(shí)候我們就會(huì)發(fā)現(xiàn)若指數(shù)位為8位撤蚊,則采用了指數(shù)偏移形式來(lái)存儲(chǔ)的指數(shù)部分實(shí)際表達(dá)的數(shù)值為[-127, 128]。我靠坐榆,這竟然違反客觀事實(shí)了拴魄!哈哈哈,其實(shí)不是這樣子的席镀,IEEE 954規(guī)定指數(shù)部分在區(qū)間[1, 2n-2]中才時(shí)減去偏正值匹中。那么0和2n-1該怎么處理呢?這個(gè)我們等下再分析『阑澹現(xiàn)在更新二進(jìn)制形式的科學(xué)計(jì)數(shù)法為(-1)S * (1.M) * 2E-偏正值顶捷。
??解釋完指數(shù)位之后,尾數(shù)位就很好理解了屎篱,就是n位二進(jìn)制位表示的無(wú)符號(hào)整數(shù)服赎,區(qū)間為[0, 2n-1]】伲現(xiàn)在我們就可以求出浮點(diǎn)數(shù)的范圍了。
- float(32位):1位符號(hào)位(S)重虑、8位指數(shù)位(E)践付、23位尾數(shù)位(M)。
???S的取值范圍:0或1缺厉; ?????? E的取值范圍:[1, 254] 永高;
???M取值范圍:[0, 223-1]; ?? 偏正值:127 - double(64位):1位符號(hào)位(S)提针、11位指數(shù)位(E)命爬、52位尾數(shù)位(M)。
???S的取值范圍:0或1辐脖; ??????E的取值范圍:[1, 2046]饲宛;
???M取值范圍:[0, 252-1];?? 偏正值:1023
??其實(shí)說到這里我們一直在解釋的都是規(guī)格形式的浮點(diǎn)數(shù)嗜价。也就是E的取值區(qū)間為 [1, 2n-2]且尾數(shù)的整數(shù)部分為1的情況艇抠。那么,對(duì)于超出這個(gè)限定的情況會(huì)如何處理呢久锥?如果大家對(duì)學(xué)過高級(jí)程序設(shè)計(jì)語(yǔ)言會(huì)知道练链,一般某個(gè)數(shù)值類中會(huì)有無(wú)窮大、NAN(Not A Number)這種特殊的情況奴拦,如Java中的Double.NAN,Double.NEGATIVE_INFINITY届吁、Double.POSITIVE_INFINITY错妖。而其實(shí)超出這個(gè)限定的部分是被用來(lái)處理特殊情況的。
??有了規(guī)格化浮點(diǎn)數(shù)疚沐,肯定還有非規(guī)格化浮點(diǎn)數(shù)暂氯。但注意:非規(guī)格化值域和規(guī)格化值域不是互補(bǔ)的,除了這兩種還有特殊值亮蛔!定義非規(guī)格式浮點(diǎn)數(shù)的E為0痴施,尾數(shù)非零且尾數(shù)的整數(shù)部分為0。這樣的話其實(shí)它就不符合科學(xué)計(jì)數(shù)法的形式了究流,但我們?nèi)匀话纯茖W(xué)計(jì)數(shù)法的形式來(lái)表達(dá)辣吃。在IEEE 954標(biāo)準(zhǔn)中規(guī)定除了規(guī)格化情況下之外的其他所有情況偏移后的值為規(guī)格情況下偏移后再加1。
??呵呵芬探,看到這里有人不禁的發(fā)出一聲冷笑神得。IEEE 754是有病啊,弄的這么惡心偷仿。其實(shí)不是這樣的哩簿,這樣做是為了解決“突然式下溢出”而誕生的(維基百科說是INTEL力薦的)宵蕉。咱們先來(lái)看看不采用IEEE 754標(biāo)準(zhǔn)所產(chǎn)生的一種惡果。假如不采用 IEEE 754节榜,E最小可以取0羡玛,以正單精度浮點(diǎn)數(shù)舉例。最小規(guī)格浮點(diǎn)數(shù)為2-127宗苍,次小規(guī)格浮點(diǎn)數(shù)為2-127 * (1 + 2-23)稼稿。它倆之間的差值為2-127 * 2-23(此時(shí)是最小差值)。而最小規(guī)格浮點(diǎn)數(shù)與0之間的差值為2-127浓若。就會(huì)出現(xiàn)一種情況渺杉,兩個(gè)相近的不相等的規(guī)格浮點(diǎn)數(shù)做差值時(shí)可能得到的數(shù)值在0和最小規(guī)格浮點(diǎn)數(shù)之間,這是不能被真實(shí)表示出來(lái)的情況挪钓,就會(huì)出現(xiàn)突然下降至0的情況是越。但如果采用了IEEE 754,最小和次小規(guī)格浮點(diǎn)數(shù)之間的差值為2-126 * 2-23碌上,但在2-126至0之間還有能被實(shí)際表示出來(lái)的浮點(diǎn)數(shù)倚评。我們不妨來(lái)求一下最小的非規(guī)格浮點(diǎn)數(shù):2-126 * 2-23。恰好等于最小相鄰規(guī)格浮點(diǎn)數(shù)之差馏予。所以這種神奇的規(guī)定是為了解決“突然式下溢出”的情況天梧。
??這時(shí)候,大家肯定有一個(gè)疑問霞丧,無(wú)論采不采用IEEE 754呢岗,能表示的范圍都是一定的,為什么會(huì)有這種區(qū)分呢蛹尝?實(shí)際上就像我們推導(dǎo)出來(lái)的float和double范圍后豫,超出這個(gè)范圍的都被認(rèn)為是非法的情況。在被規(guī)定為合法的精度范圍內(nèi)做運(yùn)算時(shí)不會(huì)發(fā)生錯(cuò)誤突那。
??除了規(guī)格化浮點(diǎn)數(shù)和非規(guī)格化浮點(diǎn)數(shù)挫酿,IEEE 954還規(guī)定了幾種特殊值。不過我們先看看這兩種非特殊值能表示的最大最小值(僅舉正數(shù))愕难。從下圖這張表中也可以看出非規(guī)格化浮點(diǎn)數(shù)的絕對(duì)值小于所有的規(guī)格化浮點(diǎn)數(shù)的絕對(duì)值早龟。
規(guī)格化 | 非規(guī)格化 | |
---|---|---|
最大 | E=254,M=223-1猫缭,S=0 (2-2-23) * 2127) |
E=0葱弟,M=223-1,S=0 ((1-2-23) * 2-126) |
最小 | E=-126猜丹,M=0翘悉,S=0 (2-126) |
E=0,M=1居触,S=0 (2-23 * 2-126) |
??說完了主要的部分妖混,我們就可以來(lái)看看最后這些特殊情況了老赤。(單精度為例)
類型 | E | M | S | 值 |
---|---|---|---|---|
正無(wú)窮 | 255 | 0 | 0 | +∞ |
負(fù)無(wú)窮 | 255 | 0 | 1 | -∞ |
NAN | 255 | 非零 | * | NAN |
??講到這里,理論部分已經(jīng)全部結(jié)束了制市。下面用Java來(lái)證明一下抬旺。(注:Java中是雙精度的,數(shù)據(jù)不一致祥楣,但道理一致)