在『代碼之謎』系列的前幾篇文章中,很多次出現(xiàn)了浮點(diǎn)數(shù)。 浮點(diǎn)數(shù)在很多編程語(yǔ)言中被稱(chēng)為簡(jiǎn)單數(shù)據(jù)類(lèi)型,其實(shí),浮點(diǎn)數(shù)比起那些復(fù)雜數(shù)據(jù)類(lèi)型(比如字符串)來(lái)說(shuō), 一點(diǎn)都不簡(jiǎn)單。
單單是說(shuō)明 IEEE浮點(diǎn)數(shù) 就可以寫(xiě)一本書(shū)了,我將用幾篇博文來(lái)簡(jiǎn)單的說(shuō)說(shuō)我所理解的浮點(diǎn)數(shù),算是拋磚引玉吧。
記得多年前我招聘 Java 程序員時(shí)的一次關(guān)于浮點(diǎn)數(shù)、二分法、編碼的面試, 多年以后,他已經(jīng)稱(chēng)為了一名很出色的程序員。 每次聚會(huì)他都會(huì)告訴我,“那次面試徹底改變了我的過(guò)去的學(xué)習(xí)方式, 我以前?只是盲目接受知識(shí),根本就沒(méi)有自己思考過(guò), 那次對(duì)話(huà),比我大學(xué)四年學(xué)到的知識(shí)都多”。
我看他簡(jiǎn)歷上寫(xiě)到讀過(guò)《信息論》才談了很多關(guān)于二分法以及編碼的話(huà)題, 整個(gè)過(guò)程大概3個(gè)小時(shí)——這是我面試時(shí)間最長(zhǎng)的一次。
因?yàn)闀r(shí)間久遠(yuǎn),我把一些我能回憶起來(lái)的關(guān)于浮點(diǎn)數(shù)的內(nèi)容整理在這篇博客中。
格式說(shuō)明:
所有我說(shuō)的話(huà),都放在引用里面。他的話(huà)放在了引號(hào)(“”)里面。沒(méi)有加引號(hào)的是我的心理活動(dòng)或者說(shuō)明。
在 8 位計(jì)算機(jī)上,浮點(diǎn)數(shù)一共有多少個(gè)呢?
“8 位的好像太過(guò)時(shí)了,現(xiàn)在主流的是 32 位的,好像可以表示3 x 10^38。”
果然不出我所料,?很多畢業(yè)生都把計(jì)算機(jī)學(xué)成了文科,他們不是在學(xué)習(xí)理論知識(shí),而是接受/背誦這些知識(shí)。
8 位計(jì)算機(jī)可以表示的整數(shù)是多少個(gè)呢?
“這個(gè)簡(jiǎn)單,2的8次方,應(yīng)該是 256 個(gè)。N 位計(jì)算機(jī)表示的整數(shù)就是 2 的 N 次方?!?/p>
他回答時(shí)顯得很興奮,因?yàn)樗K于可以反駁我的觀點(diǎn)了,他沒(méi)有把計(jì)算機(jī)當(dāng)作死記硬背的學(xué)科。
8 位計(jì)算機(jī),或者說(shuō)?8bit 可以表示 2^8 個(gè)整數(shù)。 如果用這 8bit 來(lái)表示字符,可以表示多少個(gè)呢?
“呵呵,當(dāng)然也是 2 的 8 次方了,否則就沒(méi)有必要再發(fā)明16位或者32位的 unicode 去表示漢字了?!?/p>
如你剛才所說(shuō),8bit 可以表示 3 x 10^38 個(gè)浮點(diǎn)數(shù)。那么你估算一下,2bit 可以表示多少個(gè)浮點(diǎn)數(shù)呢?
“既然 2bit 可以表示 4 個(gè)整數(shù),浮點(diǎn)數(shù)嘛肯定比這個(gè)多,最少也得能表示 10 幾個(gè)浮點(diǎn)數(shù)吧。”
好吧,按照你的思路,我說(shuō)幾個(gè)數(shù)。
- 0總該有吧,用 00 表示。
- 0.1 用 01 表示
- 0.2 用 10 表示
- 0.3 用 11 表示
現(xiàn)在你把 0.4 給我表示出來(lái)?
『他思索了片刻』“哦。我明白了,?2bit 可以表示 4 個(gè)數(shù),不管是整數(shù)、小數(shù)或者字符,就算是用 2bit 表示蘋(píng)果,我們也只能表示 4 個(gè),如果想要表示更多,就得用更多的 bit 位。”
雖然他在簡(jiǎn)歷中寫(xiě)到讀過(guò)《信息論》,他對(duì)?N bit可以表示的信息量是 2^N?肯定沒(méi)有完全理解,或者只是被動(dòng)接受了這個(gè)定理。
過(guò)了一會(huì)兒他又繼續(xù)說(shuō):“按照這個(gè)邏輯,8bit 只能表示 256 個(gè)浮點(diǎn)數(shù)了,這也太少了。 我有點(diǎn)糊涂了,浮點(diǎn)數(shù)的表示范圍一般都得幾萬(wàn)甚至幾億啊?!?/p>
于是我在 firebug 里面寫(xiě)了幾行代碼(可以在本系列第一篇的?序言?部分找到這些代碼)。
0.2 + 0.4
0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1
“這怎么可能呢?JS 居然這么不嚴(yán)格?”
顯然他把這種現(xiàn)象歸結(jié)于 js(謝天謝地,他沒(méi)有把罪過(guò)加在 firebug 身上)。 于是我用 Java 重寫(xiě)了上面的代碼,這回他只剩目瞪口呆了。
既然他已經(jīng)開(kāi)始?驚訝,那么下一步就是?思考。我又稍作了解釋?zhuān)?/p>
任何語(yǔ)言都宣稱(chēng)他們的浮點(diǎn)數(shù)的表示范圍是 3 x 10^38,這個(gè)數(shù)到底多大呢?目前所知宇宙的年齡是 1.373 x 10^10 年。
但是 32bit 最多只能表示 2^32 個(gè)數(shù),大約是 4 x 10^9。
對(duì)比一下你就會(huì)發(fā)現(xiàn)令人震驚的結(jié)果。 如果把浮點(diǎn)數(shù)的范圍比做地球,那么可以精確表示的浮點(diǎn)數(shù)還不到一粒芝麻大。
“這么說(shuō),0.2+0.4 是因?yàn)樗荒軌蚓_表示,所以出現(xiàn)了計(jì)算錯(cuò)誤的現(xiàn)象。那在編程中如何避免這種問(wèn)題呢?”
用?定點(diǎn)數(shù)表示小數(shù)。
“定點(diǎn)數(shù)不是整數(shù)嗎?定點(diǎn)數(shù)怎么表示小數(shù)???”
很顯然,有一個(gè)理論性概念錯(cuò)誤。他沒(méi)有真正理解什么是定點(diǎn),什么是浮點(diǎn)。
浮點(diǎn)數(shù)可以表示整數(shù)嗎?比如,float a = 2 可以嗎?
“可以是可以,這個(gè) 2 在計(jì)算機(jī)里面應(yīng)該存儲(chǔ)的是 2.0 吧?”
計(jì)算機(jī)肯定沒(méi)有存儲(chǔ) 2.0。百分之一萬(wàn)的肯定。計(jì)算機(jī)存儲(chǔ)的是0、1串。呵呵。
“我覺(jué)得浮點(diǎn)數(shù)應(yīng)該不會(huì)存儲(chǔ)整數(shù)的2,他存儲(chǔ)的應(yīng)該是小數(shù)的2.0,然后轉(zhuǎn)換成0、1串,是這樣嗎?”
他一連問(wèn)了我?guī)讉€(gè)問(wèn)題,使我感覺(jué)到,我不是在面試,而是在上課。
整數(shù)和小數(shù)是數(shù)學(xué)里面的概念,在計(jì)算機(jī)中,只有定點(diǎn)數(shù)和浮點(diǎn)數(shù),沒(méi)有整數(shù)和小數(shù)。
定點(diǎn)數(shù)在課本里如何定義的?
“忘了,只知道定點(diǎn)數(shù)就是整數(shù),浮點(diǎn)數(shù)就是小數(shù)。好像老師也是這么講的?!?/p>
那是因?yàn)槟銈兝蠋煵皇俏遥绻耶?dāng)老師,肯定不會(huì)這么教學(xué)生?!盒Α?/p>
定點(diǎn)、浮點(diǎn),“點(diǎn)”是什么意思?“點(diǎn)”就是小數(shù)點(diǎn)。 把小數(shù)點(diǎn)固定,通常固定在最右面,就是定點(diǎn)數(shù)。 把小數(shù)點(diǎn)浮動(dòng),就是浮點(diǎn)數(shù)。浮點(diǎn)在哪兒?這個(gè)在 IEEE 浮點(diǎn)數(shù)標(biāo)準(zhǔn)里面定義的。
回到前面話(huà)題,如何精確的表示小數(shù)呢?其中一種方案就是定點(diǎn)數(shù)。 拿 8bit 舉例吧。我們可以把小數(shù)點(diǎn)定在中間,用 4bit 表示整數(shù)部分,4bit 表示小數(shù)部分。 這樣構(gòu)造方式(專(zhuān)業(yè)點(diǎn)我們稱(chēng)他為數(shù)據(jù)結(jié)構(gòu),一般語(yǔ)言把整數(shù)和小數(shù)稱(chēng)為簡(jiǎn)單數(shù)據(jù)類(lèi)型,其實(shí)他們一點(diǎn)都不簡(jiǎn)單,而且比那些成了復(fù)合數(shù)據(jù)類(lèi)型的字符串都要復(fù)雜的多),
我們可以精確的表示64個(gè)小數(shù),我們可以精確的表示 2^8 = 256 個(gè)小數(shù)(謝謝?mfkvfn?在 iteye 上的指正)。
在下一章,我們將構(gòu)造一個(gè) 8bit 的浮點(diǎn)數(shù)表示形式,來(lái)深入探索浮點(diǎn)數(shù)不為人知的秘密。 我稱(chēng)它為 JJFN-134(JustJavac Float Notation,justjavac浮點(diǎn)數(shù)表示法),1bit符號(hào),3bit指數(shù),4bit尾數(shù)。
更多建議: