譯自:Floating point numbers are a leaky abstraction Posted on 6 April 2009 by Johnhtml
「有漏洞的抽象」(leaky abstraction)一詞源自Joel Spolsky,他用這個術語表示編程時既能讓人免受凌亂的細節所擾,但有時也會把事情弄砸的那些概念。完美的抽象是一個你永遠不須要打開的黑盒子。有漏洞的抽象則如同一個偶爾不得不打開一下的黑盒子。編程
做爲對實數的計算機表示,浮點數就是有漏洞的抽象。一般它們表現得很不錯:你甚至能夠僞裝把浮點類型當作數學上的實數。可是當這個有漏洞的抽象「露餡」的時候你卻不能這樣,雖然這種狀況不是很常見。oracle
我聽到過的最多的關於機器數的侷限性的解釋雖看起來一本正經:「因爲只有有限個浮點數,所以它們不能很好地表示實數」但這話基本沒什麼用!它沒法解釋爲何在大多數應用中浮點數確實至關好地表示了實數,也沒能對這個有漏洞的抽象可能會「露餡」給予任何提示。atom
標準浮點數大體有十進制下16位的精度,最大值爲10308次方的數量級,即1後面有308個0。(依據IEEE 754標準實現的典型浮點數。)spa
十進制16位已經至關很多了。幾乎沒有任何可測量的量能接近那麼大的精度。例如,牛頓萬有引力定律中的常數僅僅已知6位有效數字。電子的電荷已知11個有效數字,雖然比牛頓的萬有引力常數精度高得多,但仍遠低於浮點數。那麼什麼時候16位有效數字不夠呢?一個產生問題的地方就是減法。其餘的基本運算——加法、乘法、除法都很是精確。只要你不上溢或或下溢,這些操做一般會獲得直到最後一位都是正確的結果。但減法獲得的卻多是從精確到徹底不許確之間的任一種結果。若是兩個數的n位有效數字相一致,最壞的狀況下其減法可能會使n有效數字的精度所有喪失殆盡。這個問題可能出乎預料地在其餘計算中間出現。從關於計算標準誤差(calculating standard deviation)的這個帖中既可窺其一斑。在"浮點編程的5個技巧"(Five Tips for Floating Point Programming)帖子的導數計算部分,還有另一個例子。code
上溢或下溢呢?你何時會須要比10308還大的數?一般你是不會須要的。但在諸如機率之類的計算中,除非你足夠聰明,不然你始終須要它們。在機率計算中,計算一個天文數字般巨大的數與一個極其極其小的數相乘而獲得一個普通大小的乘積是件很稀鬆日常的事。最終的結果適合計算機(表示)沒什麼問題,但計算過程當中的數可能由於上溢或下溢而不適合計算機的表示。例如大多數計算機中最大的浮點數在170的階乘與171的階乘之間。如此巨大的階乘在許多應用程序中極爲日常,常出如今與其餘大階乘的比值當中。參見關於如何計算那些直接計算會致使溢出的階乘的技巧這篇帖子——"避免上溢、下溢及精度損失"(Avoiding Overflow, Underflow, and Loss of Precision)。htm
通常狀況下,你能承受對浮點算術細節的那種幸福的無知,但有時你不能。若是想了解更多,David Goldberg的論文「計算機科學家應當知道的浮點算術」(What Every Computer Scientist Should Know About Floating-Point Arithmetic)當推首選。blog
更新:見後續帖子,浮點數剖析(Anatomy of a floating point number)three
相關帖子:ip
怎樣計算二項式機率(How to compute binomial probabilities)
NaN, 1.#IND, 1.#INF, 及諸如此類的數(NaN, 1.#IND, 1.#INF, and all that)