浮點數:一種有漏洞的抽象【譯】

譯自:Floating point numbers are a leaky abstraction Posted on 6 April 2009 by John

  “有漏洞的抽象”(leaky abstraction)一詞源自Joel Spolsky,他用這個術語表示編程時既能讓人免受凌亂的細節所擾,但有時也會把事情弄砸的那些概念。完美的抽象是一個你永遠不需要打開的黑盒子。有漏洞的抽象則如同一個偶爾不得不打開一下的黑盒子。

  作爲對實數的計算機表示,浮點數就是有漏洞的抽象。通常它們表現得很不錯:你甚至可以假裝把浮點類型看成數學上的實數。但是當這個有漏洞的抽象“露餡”的時候你卻不能這樣,雖然這種情況不是很常見。

  我聽到過的最多的關於機器數的侷限性的解釋雖看起來一本正經:“由於只有有限個浮點數,因此它們不能很好地表示實數”但這話基本沒什麼用!它無法解釋爲什麼在大多數應用中浮點數確實相當好地表示了實數,也沒能對這個有漏洞的抽象可能會“露餡”給予任何提示。

  標準浮點數大致有十進制下16位的精度,最大值爲10308次方的數量級,即1後面有308個0。(依據IEEE 754標準實現的典型浮點數。)

  十進制16位已經相當不少了。幾乎沒有任何可測量的量能接近那麼大的精度。例如,牛頓萬有引力定律中的常數僅僅已知6位有效數字。電子的電荷已知11個有效數字,雖然比牛頓的萬有引力常數精度高得多,但仍遠低於浮點數。那麼何時16位有效數字不夠呢?一個產生問題的地方就是減法。其他的基本運算——加法、乘法、除法都非常精確。只要你不上溢或或下溢,這些操作通常會得到直到最後一位都是正確的結果。但減法得到的卻可能是從精確到完全不準確之間的任一種結果。如果兩個數的n位有效數字相一致,最壞的情況下其減法可能會使n有效數字的精度全部喪失殆盡。這個問題可能出乎預料地在其他計算中間出現。從關於計算標準偏差(calculating standard deviation)的這個帖中既可窺其一斑。在"浮點編程的5個技巧"(Five Tips for Floating Point Programming)帖子的導數計算部分,還有另外一個例子。

  上溢或下溢呢?你什麼時候會需要比10308還大的數?通常你是不會需要的。但在諸如概率之類的計算中,除非你足夠聰明,否則你始終需要它們。在概率計算中,計算一個天文數字般巨大的數與一個極其極其小的數相乘而得到一個普通大小的乘積是件很稀鬆平常的事。最終的結果適合計算機(表示)沒什麼問題,但計算過程中的數可能因爲上溢或下溢而不適合計算機的表示。例如大多數計算機中最大的浮點數在170的階乘與171的階乘之間。如此巨大的階乘在許多應用程序中極爲平常,常出現在與其他大階乘的比值當中。參見關於如何計算那些直接計算會導致溢出的階乘的技巧這篇帖子——"避免上溢、下溢及精度損失"(Avoiding Overflow, Underflow, and Loss of Precision)。

  一般情況下,你能承受對浮點算術細節的那種幸福的無知,但有時你不能。如果想了解更多,David Goldberg的論文“計算機科學家應當知道的浮點算術”(What Every Computer Scientist Should Know About Floating-Point Arithmetic)當推首選。

更新:見後續帖子,浮點數剖析(Anatomy of a floating point number)

相關帖子

怎樣計算二項式概率(How to compute binomial probabilities)
NaN, 1.#IND, 1.#INF, 及諸如此類的數(NaN, 1.#IND, 1.#INF, and all that)


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章