首頁 > 後端開發 > C++ > 為什麼啟用編譯器最佳化後,我的浮點舍入程式碼會產生不同的結果?

為什麼啟用編譯器最佳化後,我的浮點舍入程式碼會產生不同的結果?

Susan Sarandon
發布: 2024-11-14 19:39:02
原創
920 人瀏覽過

Why Does My Floating-Point Rounding Code Produce Different Results with Compiler Optimizations Enabled?

啟用最佳化的浮點舍入差異:編譯器錯誤或最佳化困境?

浮點計算通常會表現出意外的行為,尤其是在啟用編譯器最佳化。考慮以下程式碼片段:

#include <cstdlib>
#include <iostream>
#include <cmath>

double round(double v, double digit)
{
    double pow = std::pow(10.0, digit);
    double t = v * pow;
    double r = std::floor(t + 0.5);
    return r / pow;
}

int main()
{
    std::cout << round(4.45, 1) << std::endl;
    std::cout << round(4.55, 1) << std::endl;
}
登入後複製

預期輸出:

4.5
4.6
登入後複製

但是,當使用具有最佳化(O1 - O3) 的g 編譯此程式碼時,輸出變為:

4.5
4.5
登入後複製

原因差異:

這種不一致源自於 x86 處理器內部使用 80 位元擴充精度進行浮點計算。然而,雙精度變數通常是 64 位元寬。當浮點值從 CPU 暫存器儲存到記憶體時,它們會從 80 位元精度舍入到 64 位元精度。此舍入可能會引入輕微錯誤。

最佳化程度的影響:

不同的最佳化等級可能會影響浮點值儲存到記憶體的頻率。優化等級越高,這種情況發生的頻率就越高。結果,舍入誤差變得更加明顯。

解決方案:

  1. 使用-ffloat-store GCC 選項: 這個選項指示編譯器將浮點變數儲存在記憶體中而不是寄存器中。這會強制在不同的最佳化層級上一致地進行舍入。
  2. 使用 long double 類型: long double 在 g 上通常為 80 位元寬。使用這種類型可以完全避免舍入問題。
  3. 修改變數儲存:將中間運算結果儲存到變數中,以最小化捨入誤差。

進一步的考慮因素:

  • Intel x86_64 版本是受此問題影響較小,因為編譯器預設使用SSE 暫存器來表示float和double,從而無需擴展精度。
  • -mfpmath 編譯器選項可用來控制 x86_64 建置中使用的浮點精確度。
  • 是否始終開啟 -ffloat-store 選項取決於特定應用及其對浮點精確度的敏感度。對於關鍵應用程序,使用此選項來確保結果一致可能是明智之舉。
  • 調查現有 C 程式碼和函式庫是否有潛在問題可能非常耗時。考慮使用工具或實施測試來檢測和解決任何浮點精確度問題。

以上是為什麼啟用編譯器最佳化後,我的浮點舍入程式碼會產生不同的結果?的詳細內容。更多資訊請關注PHP中文網其他相關文章!

來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
作者最新文章
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板