我正在寫一個模擬程序,以分立的步驟進行。仿真由許多節點組成,每個節點都有一個與其相關的浮點值,並在每個步驟重新計算。結果可以是正數,負數或零。如何在使用浮動時獲得一致的程序行爲?
在結果爲零或更少的情況下發生的情況。到目前爲止,這看起來很簡單 - 我可以做這樣的事情對每個節點:
if (value <= 0.0f) something_happens();
一個問題但是出現了,經過最近我在我重新安排的順序,節目做出了一些改變,其中某些計算已完成。在完美的世界中,這些重新排列後的值仍然會出現,但由於浮點表示的不精確性,它們會出現非常不同的結果。由於每個步驟的計算取決於前一步驟的結果,因此隨着模擬的進行,結果中的這些輕微變化可以累積成更大的變化。
以下是一個演示的現象,我描述一個簡單的例子程序:
float f1 = 0.000001f, f2 = 0.000002f;
f1 += 0.000004f; // This part happens first here
f1 += (f2 * 0.000003f);
printf("%.16f\n", f1);
f1 = 0.000001f, f2 = 0.000002f;
f1 += (f2 * 0.000003f);
f1 += 0.000004f; // This time this happens second
printf("%.16f\n", f1);
這個程序的輸出是
0.0000050000057854
0.0000050000062402
即使加法是可交換所以無論結果應該是相同的。注意:我完全理解爲什麼會發生這種情況 - 這不是問題。問題在於,這些變化可能意味着有時候一個在步驟N中出現負值的值會觸發something_happens(),現在可能會提前一兩步出現負值,這可能會導致非常不同的整體模擬結果,因爲something_happens()有很大的作用。
我想知道的是,是否有一個很好的方法來決定何時觸發something_happens(),這將不會受到重新排序操作導致的計算結果中的微小變化的影響,從而使行爲更新版本的程序將與舊版本保持一致。
唯一的解決辦法,我至今能想到的是使用一些價值小量這樣的:
if (value < epsilon) something_happens();
但由於結果的微小變化隨着時間積累,我需要做出相當小量大(相對來說),以確保變化不會導致something_happens()在不同的步驟被觸發。有沒有更好的辦法?
我讀過this excellent article浮點比較,但我沒有看到任何比較方法描述可以幫助我在這種情況下。
注意:使用整數值不是一個選項。
編輯用雙打,而不是浮動的可能性已經提高。這不會解決我的問題,因爲變化仍然存在,他們只是一個較小的數量級。
如果微小的變化會導致輸出的巨大變化,是不是隻是告訴你,你的結果準確度低? (另外:爲什麼float不是double?) – 2012-03-08 05:34:37
小心:'printf(「%。16f \ n」,f1);'這是一個意想不到的副作用:它會將你的float轉換爲一個加上無效數字的double。我認爲,浮點數最多爲7位精度。 – 2012-03-08 05:52:25
標準的方法是對浮點值進行求和的方法是:對它們進行排序並從最小值到最大值進行求和,這樣就會降低最低精度。也使用雙不浮動。 – 2012-03-08 05:54:04