浮點數運算與數值表示

Inf 與 NaN

/ INF and NAN (rhymes with 'can') /

對於 1.0/0.0、或 sqrt(-1.0)、或一個真的大到存不下的上溢結果,電腦應該回傳什麼?為每個這類事件就讓整個程式停擺,未免太殘酷。IEEE 754 改為保留兩類特殊值來優雅地表示這些結果:Inf,一個帶號的無窮,用於太大或本就是數學無窮的結果;以及 NaN(非數,Not a Number),用於真正未定義的結果。

Inf 有 +Inf 與 -Inf,行為像個合理的極限:1.0/0.0 = +Inf、1.0/(-0.0) = -Inf、Inf + 1 = Inf、1.0/Inf = 0。NaN 則由真正不定的運算產生:0.0/0.0、Inf - Inf、sqrt(-1.0)、0.0*Inf。兩者都以保留的全 1 指數樣式編碼(Inf 的小數部分為零,NaN 的則非零)。它們的決定性特徵是「傳播」:幾乎任何牽涉 NaN 的算術運算都回傳 NaN,於是單一未定義值會往下游流動、汙染它所觸及的一切——這其實有用,它「標記」出哪裡出了錯,而非把錯誤藏在一個看似合理的數字背後。

NaN 有一條著名而出人意表的規則:它不等於任何東西,包括它自己。判斷式 (x != x) 恰好在 x 為 NaN 時為真,這是偵測 NaN 的標準、可移植的方法。這條規則也使 NaN 成為比較與排序的無聲破壞者:max(3, NaN) 可能回傳任一運算元,視函式庫而定,而一個溜進你資料的 NaN 能讓「已排序」的陣列變得荒謬、或讓收斂判斷永不觸發。當你在除錯一個產出垃圾的數值程式時,第一步往往是去獵捕第一個 NaN 或 Inf 的誕生之處。

一個寫成「while (residual > tol)」的停止判斷,若 residual 一旦變成 NaN 就會永遠迴圈,因為 NaN > tol 為假、且 NaN <= tol 也為假——與 NaN 的比較永遠為假。在信任迴圈的結束條件之前,用 (residual != residual) 或 isnan() 檢查明確偵測它。

NaN != NaN——偵測它的標準技巧。

Inf 與 NaN 是特性,不是當機:它們讓計算得以繼續並「示警」,而非把麻煩藏起來。但由於與 NaN 的每個比較都為假,NaN 能無聲地破壞迴圈、排序與判斷——切勿假設「x >= 0 否則 x < 0」涵蓋了所有情況。

又称
infinity and not-a-numberInfinityNaN無窮與非數