看板 C_and_CPP 關於我們 聯絡資訊
※ 引述《lovejomi (JOMI)》之銘言: : a. stackoverflow的作法錯了? : b. 為什麼會把差值當成相等? : c. 到底這個epsilon 最應該用在哪裡呢? : d. 是不是把almost_equal當成一個正解 才是正確的浮點數比較相等呢? : 我用以下tool 把 epsilon 看他hex form 反推一下 : 他是2^-23 = 0.00000011920928955078125f乍看之下跟gcc定義一致 : https://www.h-schmidt.net/FloatConverter/IEEE754.html : 觀念上有些錯誤 : 請大家修正一下 : 謝謝 浮點數雖然可能因為實作不同而有差異性,但扣除一些特殊的值之後 浮點數型別能毫無誤差表示的數值跟整數能表示的數值性質相似 都是落在數線上的離散格子點. 跟整數不同的地方在於,相鄰的整數格子點間的距離是相等的 但是相鄰的浮點數格子點間的距離可能不相等 (與指數的大小和 epsilon 有關: epsilon * base^exp) 整數: | | | | | | | | | | (相鄰的整數間都是等寬) 浮點數: | | | | | | | | | | | | | | | (相鄰的浮點數間距離,會隨著浮點數的大小而可能有所不同) 要透過浮點數做運算時大概有三個步驟 1. 將要計算的數字轉換成浮點數可精確表示的格子點 (可能產生誤差) 2. 對這些在格子點上的數字們做運算 3. 將算出來的結果存在浮點數可精確表示的格子點上 (可能產生誤差) 以對 x + y 做計算為例. 因為 x 或 y 可能不是目前所用浮點數實作可以精確表達的數字 (例如 0.1 在常見的浮點數實作上無法準確落在格子點上) 所以首先要將 x 與 y 轉換到各自最近的浮點數格子點上,此時就產生誤差 (例如 0.1 + 0.1 時會先將兩個 0.1 都轉換到最近的浮點數格子點,造成運算完的結果可能不完全等於 0.2) 假設將 x 轉換到 x', y 轉換到 y',則令 x = x' + dx (dx 表示 x 轉換到格子點 x' 產生的誤差) y = y' + dy (dy 表示 y 轉換到格子點 y' 產生的誤差) 因此當我們運算 x + y 時,實質上是用 x' + y' 去運算 同時運算的結果,也可能不落在格子點上 假設我們將 x' + y' 運算的結果取名為 z,則將 z 轉換到最近的格子點 z' 的話 z = z' + dz (dz 表示 z 轉換到格子點 z' 產生的誤差) 綜合以上所說, 原本我們想要算 x + y ,也就是 (x' + dx) + (y' + dy) = x' + y' + dx + dy (例如 0.1 + 0.1) 最後卻變成是算出 z', 也就是 z - dz = x' + y' - dz (例如 0.1 + 0.1 的結果) 最後原本想算的和與真正算出的結果間的誤差就變成是 |x + y - z'| = |dx + dy + dz| 如果 |dx + dy + dz| == 0 的話,去比較 x + y == z' 通常是沒問題 但是不為 0 的時候,dx + dy + dz 和的大小範圍就會影響我們判斷是否可能相等時的條件範圍 而我們怎麼知道 dx + dy + dz 的大小範圍呢 ? 這和當時 x, y, z 的大小有關 因為誤差大小通常不會大於浮點數格子點間一半的距離 而浮點數格子點間距離會隨指數與 epsilon 不同而不同 epsilon 跟浮點數實作有關,而指數大小則與目前的值大小有關 這邊 epsilon 表示的是 1 跟下一個比 1 大的浮點數格子點的距離 可以想成不考慮 base^exp 的大小的情況下 (exp = 0),兩個浮點數格子點間最近的距離 (當然這裡我們去掉了一些特殊的值,例如 subnormal number, +-0) 但是因為 base^exp 會放大格子間距離,所以格子間距離要多乘上 base^exp 而與 epsilon * base^exp 有關 因此考慮 dx + dy + dz 這誤差可能的範圍時,要考慮 dx, dy, dz 的大小 而 dx, dy, dz 的大小與 x, y, z 當時的值有關 (影響 base^exp 的大小) 此外還要考慮 epsilon 跟允許的誤差計算次數等 當然我知道我這邊舉 0.1 當例子可能不好 (通常是 subnormal number),只是為了方便理解 草草寫寫,有錯誤疏漏也請不吝指教 ※ 編輯: Feis (140.122.83.198), 09/19/2018 00:47:44
cutekid: 大推(Y) 09/19 03:16
chchwy: 神解答 格子點的解釋很好懂 09/19 08:03
sarafciel: 推格子點的解釋 09/20 15:14
lovejomi: 針對那三個步驟,算的時候沒有受到誤差限制反而是算完 09/25 16:59
lovejomi: 之後3. 會因為要mapping to ieee754而產生誤差 這邊覺 09/25 16:59
lovejomi: 得很神奇 不知道cpu怎麼運算的 09/25 16:59