Share via


/fp (指定浮點行為)

指定編譯器如何處理浮點運算式、優化和例外狀況。 /fp選項會指定產生的程式碼是否允許浮點環境變更為四捨五入模式、例外狀況遮罩和次正規行為,以及浮點狀態檢查是否傳回目前、精確的結果。 它會控制編譯器是否會產生維護來源作業和運算式順序的程式碼,並符合 NaN 傳播的標準。 或者,如果它會產生更有效率的程式碼,可能會重新排序或合併作業,並使用簡化 IEEE-754 標準不允許的代數轉換。

語法

/fp:contract
/fp:except[-]
/fp:fast
/fp:precise
/fp:strict

/fp:except[-]
/fp:fast
/fp:precise
/fp:strict

引數

/fp:contract

選項 /fp:contract 可讓編譯器在指定 /fp:precise/fp:except 選項時產生浮點合約。 收縮 是結合浮點運算的電腦指令,例如 Fused-Multiply-Add (FMA)。 FMA 定義為 IEEE-754 的基本運算,不會在加法之前四捨五入中繼乘積,因此結果可能與不同的乘法和加法運算不同。 由於它是實作為單一指令,所以其速度可能會比個別的指令更快。 速度代價是位確切的結果,而且無法檢查中繼值。

根據預設, /fp:fast 選項會啟用 /fp:contract 。 選項 /fp:contract 與 不相容 /fp:strict

此選項 /fp:contract 是 Visual Studio 2022 的新功能。

/fp:precise

根據預設,編譯器會使用 /fp:precise 行為。

在 下 /fp:precise ,編譯器會在產生並優化目的機器的物件程式碼時,保留浮點程式碼的來源運算式排序和四捨五入屬性。 編譯器會在運算式評估期間四個特定點四捨五入原始程式碼精確度:在指派、類型廣播、浮點引數傳遞至函式呼叫,以及當函式呼叫傳回浮點值時。 中繼計算可能以機器精確度執行。 Typecasts 可用來明確四捨五入中繼計算。

編譯器不會在浮點運算式上執行代數轉換,例如重新關聯或分佈,除非它可以保證轉換會產生位相同的結果。 涉及特殊值的運算式(NaN、+infinity、-infinity、-infinity、-0.0)會根據 IEEE-754 規格進行處理。 例如, x != x 評估為 true 如果 x 是 NaN。 浮點收縮預設不會在 下 /fp:precise 產生。 此行為是 Visual Studio 2022 的新功能。 舊版編譯器預設可能會在 下 /fp:precise 產生合約。

編譯器不會在浮點運算式上執行代數轉換,例如重新關聯或分佈,除非它可以保證轉換會產生位相同的結果。 涉及特殊值的運算式(NaN、+infinity、-infinity、-infinity、-0.0)會根據 IEEE-754 規格進行處理。 例如, x != x 評估為 true 如果 x 是 NaN。 浮點收縮可能會在 下 /fp:precise 產生。

編譯器會產生程式碼,以在預設浮點環境中 執行 。 它也假設在執行時間不會存取或修改浮點環境。 也就是說,它會假設程式碼:將浮點例外狀況保留遮罩、不會讀取或寫入浮點狀態暫存器,也不會變更四捨五入模式。

如果您的浮點程式碼不相依于浮點語句中的作業和運算式順序(例如,如果您不在乎是否 a * b + a * c 計算為 (b + c) * a2 * a 作為 a + a ),請考慮 /fp:fast 選項,這可以產生更快、更有效率的程式碼。 如果您的程式碼都相依于作業和運算式的順序,並存取或改變浮點環境(例如,若要變更四捨五入模式或設陷浮點例外狀況),請使用 /fp:strict

/fp:strict

/fp:strict 具有類似 /fp:precise 的行為,也就是說,編譯器會在產生並優化目的機器的物件程式碼時,保留浮點程式碼的來源排序和四捨五入屬性,並在處理特殊值時觀察標準。 程式也可以在執行時間安全地存取或修改浮點環境。

在 下 /fp:strict ,編譯器會產生程式碼,讓程式安全地解除遮罩浮點例外狀況、讀取或寫入浮點狀態暫存器,或變更四捨五入模式。 它會在運算式評估期間四個特定點四捨五入原始程式碼精確度:在指派、類型廣播、浮點引數傳遞至函式呼叫時,以及當函式呼叫傳回浮點值時。 中繼計算可能以機器精確度執行。 Typecasts 可用來明確四捨五入中繼計算。 編譯器不會在浮點運算式上進行任何代數轉換,例如重新關聯或分佈,除非它可以保證轉換會產生位相同的結果。 涉及特殊值的運算式(NaN、+infinity、-infinity、-infinity、-0.0)會根據 IEEE-754 規格進行處理。 例如, x != x 評估為 true 如果 x 是 NaN。 浮點收縮不會在 下 /fp:strict 產生。

/fp:strict 比計算成本更高 /fp:precise ,因為編譯器必須插入額外的指令來攔截例外狀況,並允許程式在執行時間存取或修改浮點環境。 如果您的程式碼未使用這項功能,但需要原始程式碼排序和四捨五入,或依賴特殊值,請使用 /fp:precise 。 否則,請考慮使用 /fp:fast ,這會產生更快且較小的程式碼。

/fp:fast

選項 /fp:fast 可讓編譯器重新排序、合併或簡化浮點運算,以優化速度與空間的浮點程式碼。 編譯器可能會省略指派語句、typecasts 或函式呼叫的四捨五入。 例如,它可以使用關聯和散發法來重新排序作業或進行代數轉換。 即使這類轉換會導致明顯不同的四捨五入行為,它也可能會重新排序程式代碼。 由於這項增強的優化,某些浮點運算的結果可能會與其他選項所產生的 /fp 不同。 特殊值(NaN、+infinity、-infinity、-0.0)可能不會根據 IEEE-754 標準傳播或行為嚴格。 浮點收縮可能會在 下 /fp:fast 產生。 編譯器仍然受到 底下 /fp:fast 的基礎架構所系結,而且可以使用 選項來取得 /arch 更多優化。

在 下 /fp:fast ,編譯器會產生要在預設浮點環境中執行的程式碼,並假設在執行時間不會存取或修改浮點環境。 也就是說,它會假設程式碼:將浮點例外狀況保留遮罩、不會讀取或寫入浮點狀態暫存器,也不會變更四捨五入模式。

/fp:fast 適用于不需要嚴格原始程式碼排序和四捨五入浮點運算式的程式,而且不依賴標準規則來處理特殊值,例如 NaN 。 如果您的浮點程式碼需要保留原始程式碼排序和四捨五入,或依賴特殊值的標準行為,請使用 /fp:precise 。 如果您的程式碼存取或修改浮點環境以變更四捨五入模式、取消遮罩浮點例外狀況,或檢查浮點狀態,請使用 /fp:strict

/fp:except

選項 /fp:except 會產生程式碼,以確保任何未遮罩的浮點例外狀況都會在發生的確切點引發,而且不會引發任何其他浮點例外狀況。 根據預設, /fp:strict 選項會啟用 /fp:except ,而且 /fp:precise 不會。 選項 /fp:except 與 不相容 /fp:fast 。 選項可以使用 來明確停用 /fp:except-

本身不會 /fp:except 啟用任何浮點例外狀況。 不過,程式必須啟用浮點例外狀況。 如需如何啟用浮點例外狀況的詳細資訊,請參閱 _controlfp

備註

您可以在相同的編譯器命令列中指定多個 /fp 選項。 一次只能有一個 /fp:strict/fp:fast/fp:precise 選項生效。 如果您在命令列上指定其中一個以上的選項,則較新的選項會優先使用,編譯器會產生警告。 /fp:strict/fp:except 選項與 不相容 /clr

/Za[ANSI 相容性] 選項與 不相容 /fp

使用編譯器指示詞來控制浮點行為

編譯器提供三個 pragma 指示詞來覆寫命令列上指定的浮點行為: float_controlfenv_accessfp_contract 。 您可以使用這些指示詞來控制函式層級的浮點行為,而不是在函式內。 這些指示詞不會直接對應至 /fp 選項。 下表顯示選項和 pragma 指示詞如何 /fp 彼此對應。 如需詳細資訊,請參閱個別選項和 pragma 指示詞的檔。

選項 float_control(precise, *) float_control(except, *) fenv_access(*) fp_contract(*)
/fp:fast off off off on
/fp:precise on off off off*
/fp:strict on on on off

* 在 Visual Studio 2022 之前的 Visual Studio 版本中, /fp:precise 行為預設為 fp_contract(on)

選項 float_control(precise, *) float_control(except, *) fenv_access(*) fp_contract(*)
/fp:fast off off off on
/fp:precise on off off on*
/fp:strict on on on off

* 從 Visual Studio 2022 開始的 Visual Studio 版本中, /fp:precise 行為預設為 fp_contract(off)

預設浮點環境

初始化進程時,會 設定預設浮點環境 。 此環境會遮罩所有浮點例外狀況、將四捨五入模式設定為四捨五入至最接近( FE_TONEAREST )、保留次正規值(反正規值) floatdouble 值、使用 、、 和 long double 值的預設精確度,以及支援的位置,將無限控制項設定為預設的仿射模式。

浮點環境存取和修改

Microsoft Visual C++ 執行時間提供數個函式來存取和修改浮點環境。 這些包括 _controlfp_clearfp_statusfp 及其變體。 若要在程式碼存取或修改浮點環境時確保正確的程式列為, fenv_access 必須透過 /fp:strict 選項或使用 fenv_access pragma 來啟用,這些函式才能有任何作用。 未啟用時 fenv_access ,浮點環境的存取或修改可能會導致非預期的程式列為:

  • 程式碼可能不接受對浮點環境的要求變更,

  • 浮點狀態暫存器可能不會報告預期或目前的結果,

  • 可能發生非預期的浮點例外狀況,或可能發生預期的浮點例外狀況。

當您的程式碼存取或修改浮點環境時,當您結合已啟用的程式碼與未 fenv_access 啟用的程式碼 fenv_access 時,必須小心。 在未啟用的程式碼 fenv_access 中,編譯器會假設平臺預設浮點環境有效。 它也會假設不會存取或修改浮點狀態。 建議您先將本機浮點環境儲存並還原為其預設狀態,再將控制項傳送至未 fenv_access 啟用的函式。 此範例示範如何 float_control 設定和還原 pragma:

#pragma float_control(precise, on, push)
// Code that uses /fp:strict mode
#pragma float_control(pop)

浮點四捨五入模式

/fp:precise在 和 /fp:fast 下,編譯器會產生程式碼,以在預設浮點環境中執行。 它假設環境不會在執行時間存取或修改。 也就是說,編譯器會假設程式碼永遠不會解除遮罩浮點例外狀況、讀取或寫入浮點狀態暫存器,或變更四捨五入模式。 不過,有些程式需要改變浮點環境。 例如,此範例會藉由改變浮點四捨五入模式來計算浮點乘法的錯誤界限:

// fp_error_bounds.cpp
#include <iostream>
#include <limits>
using namespace std;

int main(void)
{
    float a = std::<float>::max();
    float b = -1.1;
    float cLower = 0.0;
    float cUpper = 0.0;
    unsigned int control_word = 0;
    int err = 0;

    // compute lower error bound.
    // set rounding mode to -infinity.
    err = _controlfp_s(&control_word, _RC_DOWN, _MCW_RC);
    if (err)
    {
        cout << "_controlfp_s(&control_word, _RC_DOWN, _MCW_RC) failed with error:" << err << endl;
    }  
    cLower = a * b;

    // compute upper error bound.
    // set rounding mode to +infinity.
    err = _controlfp_s(&control_word, _RC_UP, _MCW_RC);
    if (err)
    {
        cout << "_controlfp_s(&control_word, _RC_UP, _MCW_RC) failed with error:" << err << endl;
    }
    cUpper = a * b;

    // restore default rounding mode.
    err = _controlfp_s(&control_word, _CW_DEFAULT, _MCW_RC);
    if (err)
    {
        cout << "_controlfp_s(&control_word, _CW_DEFAULT, _MCW_RC) failed with error:" << err << endl;
    }
    // display error bounds.
    cout << "cLower = " << cLower << endl;
    cout << "cUpper = " << cUpper << endl;
    return 0;
}

由於編譯器假設 和 /fp:precise/fp:fast 的預設浮點環境,因此可以忽略 對 _controlfp_s 的呼叫。 例如,使用 和 /fp:precise 來編譯 /O2 x86 架構時,不會計算界限,而且範例程式會輸出:

cLower = -inf
cUpper = -inf

/O2使用 和 /fp:strict 編譯 x86 架構時,範例程式會輸出:

cLower = -inf
cUpper = -3.40282e+38

浮點特殊值

在 和 /fp:strict/fp:precise ,涉及特殊值的運算式 (NaN, +infinity, -infinity, -0.0) 會根據 IEEE-754 規格運作。 在 下 /fp:fast ,這些特殊值的行為可能與 IEEE-754 不一致。

此範例示範 、 /fp:strict/fp:fast 底下 /fp:precise 特殊值的不同行為:

// fp_special_values.cpp
#include <stdio.h>
#include <cmath>

float gf0 = -0.0;

int main()
{
    float f1 = INFINITY;
    float f2 = NAN;
    float f3 = -INFINITY;
    bool a, b;
    float c, d, e;
    a = (f1 == f1);
    b = (f2 == f2);
    c = (f1 - f1);
    d = (f2 - f2);
    e = (gf0 / f3);
    printf("INFINITY == INFINITY : %d\n", a);
    printf("NAN == NAN           : %d\n", b);
    printf("INFINITY - INFINITY  : %f\n", c);
    printf("NAN - NAN            : %f\n", d);
    printf("std::signbit(-0.0/-INFINITY): %d\n", std::signbit(e));
    return 0;
}

使用 /O2 /fp:precise/O2 /fp:strict 進行 x86 架構編譯時,輸出會與 IEEE-754 規格一致:

INFINITY == INFINITY : 1
NAN == NAN           : 0
INFINITY - INFINITY  : -nan(ind)
NAN - NAN            : nan
std::signbit(-0.0/-INFINITY): 0

使用 /O2 /fp:fast ** 進行 x86 架構編譯時,輸出與 IEEE-754 不一致:

INFINITY == INFINITY : 1
NAN == NAN           : 1
INFINITY - INFINITY  : 0.000000
NAN - NAN            : 0.000000
std::signbit(-0.0/-INFINITY): 0

浮點代數轉換

在 和 /fp:strict/fp:precise ,除非保證轉換會產生位相同的結果,否則編譯器不會執行任何數學轉換。 編譯器可能會在 下 /fp:fast 進行這類轉換。 例如,範例 algebraic_transformation 函式中的運算式 a * b + a * c 可能會在 下 /fp:fast 編譯成 a * (b + c) 。 這類轉換不會在 或 /fp:strict/fp:precise 完成,編譯器會產生 a * b + a * c

float algebraic_transformation (float a, float b, float c)
{
    return a * b + a * c;
}

浮點明確轉換點

在 和 /fp:strict/fp:precise ,編譯器會在運算式評估期間四個特定點四捨五入原始程式碼精確度:在指派、類型廣播、浮點引數傳遞至函式呼叫,以及當函式呼叫傳回浮點值時。 Typecasts 可用來明確四捨五入中繼計算。 在 下 /fp:fast ,編譯器不會在這些點產生明確的轉換,以確保原始程式碼精確度。 此範例示範不同 /fp 選項下的行為:

float casting(float a, float b)
{
    return 5.0*((double)(a+b));
}

使用 /O2 /fp:precise/O2 /fp:strict 編譯時,您可以看到在 typecast 和函式傳回點插入明確型別轉換,並插入 x64 架構產生的程式碼:

        addss    xmm0, xmm1
        cvtss2sd xmm0, xmm0
        mulsd    xmm0, QWORD PTR __real@4014000000000000
        cvtsd2ss xmm0, xmm0
        ret      0

在產生的程式碼下 /O2 /fp:fast 會簡化,因為所有類型轉換都會優化:

        addss    xmm0, xmm1
        mulss    xmm0, DWORD PTR __real@40a00000
        ret      0

在 Visual Studio 開發環境中設定這個編譯器選項

  1. 開啟專案的 [屬性頁] 對話方塊。 如需詳細資料,請參閱在 Visual Studio 中設定 C ++ 編譯器和組建屬性

  2. 選取 [ 組態屬性 > C/C++ > 程式碼產生 ] 屬性頁。

  3. 修改浮點模型 屬性。

若要以程式方式設定這個編譯器選項

另請參閱

MSVC 編譯器選項
MSVC 編譯器命令列語法