Udostępnij za pośrednictwem


/fp (Określ zachowanie zmiennoprzecinkowe)

Określa sposób traktowania przez kompilator wyrażeń zmiennoprzecinkowych, optymalizacji i wyjątków. Opcje /fp określają, czy wygenerowany kod umożliwia zmianę środowiska zmiennoprzecinkowego w trybie zaokrąglania, maskach wyjątków i zachowaniu podrzędnym oraz czy sprawdzanie stanu zmiennoprzecinkowe zwraca bieżące, dokładne wyniki. Określa, czy kompilator generuje kod, który obsługuje operację źródłową i kolejność wyrażeń, i jest zgodny ze standardem propagacji NaN. Ewentualnie, jeśli zamiast tego generuje bardziej wydajny kod, który może zmienić kolejność lub połączyć operacje i użyć upraszczających transformacji algebraicznych, które nie są dozwolone przez standard IEEE-754.

Składnia

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

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

Argumenty

/fp:contract

Opcja /fp:contract umożliwia kompilatorowi generowanie skurczów zmiennoprzecinkowych podczas określania /fp:precise opcji i /fp:except . Skurcz to instrukcja maszynowa, która łączy operacje zmiennoprzecinkowe, takie jak Fused-Multiply-Add (FMA). FMA, zdefiniowana jako podstawowa operacja przez IEEE-754, nie zaokrągla produktu pośredniego przed dodatkami, więc wynik może różnić się od oddzielnych operacji mnożenia i dodawania. Ponieważ jest ona implementowana jako pojedyncza instrukcja, może być szybsza niż oddzielne instrukcje. Szybkość jest kosztem bitowych dokładnych wyników i niezdolności do zbadania wartości pośredniej.

Domyślnie /fp:fast opcja włącza /fp:contractwartość . Opcja nie jest zgodna /fp:contract z /fp:strict.

Opcja /fp:contract jest nowa w programie Visual Studio 2022.

/fp:precise

Domyślnie kompilator używa /fp:precise zachowania.

W obszarze /fp:precisekompilator zachowuje kolejność wyrażeń źródłowych i zaokrąglanie właściwości kodu zmiennoprzecinkowego podczas generowania i optymalizowania kodu obiektu dla maszyny docelowej. Kompilator zaokrągla do dokładności kodu źródłowego w czterech określonych punktach podczas obliczania wyrażenia: przy przypisaniach, emisjach typów, gdy argumenty zmiennoprzecinkowe są przekazywane do wywołania funkcji, a wywołanie funkcji zwraca wartość zmiennoprzecinkowa. Obliczenia pośrednie mogą być wykonywane z dokładnością maszyny. Emisje typu mogą służyć do jawnego zaokrąglić obliczenia pośrednie.

Kompilator nie wykonuje przekształceń algebraicznych w wyrażeniach zmiennoprzecinkowych, takich jak ponowne kojarzenie lub rozkład, chyba że może zagwarantować, że transformacja generuje bitowo identyczny wynik. Wyrażenia obejmujące specjalne wartości (NaN, +infinity, -infinity, -0,0) są przetwarzane zgodnie ze specyfikacjami IEEE-754. Na przykład x != x funkcja ocenia, true czy x jest to NaN. Skurcze zmiennoprzecinkowe nie są domyślnie generowane w obszarze /fp:precise. To zachowanie jest nowe w programie Visual Studio 2022. Poprzednie wersje kompilatora mogą domyślnie generować skurcze w obszarze /fp:precise.

Kompilator nie wykonuje przekształceń algebraicznych w wyrażeniach zmiennoprzecinkowych, takich jak ponowne kojarzenie lub rozkład, chyba że może zagwarantować, że transformacja generuje bitowo identyczny wynik. Wyrażenia obejmujące specjalne wartości (NaN, +infinity, -infinity, -0,0) są przetwarzane zgodnie ze specyfikacjami IEEE-754. Na przykład x != x funkcja ocenia, true czy x jest to NaN. Skurcze zmiennoprzecinkowe mogą być generowane w obszarze /fp:precise.

Kompilator generuje kod przeznaczony do uruchomienia w domyślnym środowisku zmiennoprzecinkowym. Założono również, że środowisko zmiennoprzecinkowe nie jest dostępne ani modyfikowane w czasie wykonywania. Zakłada to, że kod: pozostawia wyjątki zmiennoprzecinkowe maskowane, nie odczytuje ani nie zapisuje rejestrów stanu zmiennoprzecinkowych i nie zmienia trybów zaokrąglania.

Jeśli kod zmiennoprzecinkowa nie zależy od kolejności operacji i wyrażeń w instrukcjach zmiennoprzecinkowych (na przykład jeśli nie obchodzi cię, czy a * b + a * c jest obliczany jako (b + c) * a lub 2 * a jako a + a), rozważ /fp:fast opcję, która może szybciej generować, wydajniejszy kod. Jeśli kod zależy od kolejności operacji i wyrażeń, a także uzyskuje dostęp do środowiska zmiennoprzecinkowego lub zmienia je (na przykład w celu zmiany trybów zaokrąglania lub wychwytowania wyjątków zmiennoprzecinkowych), użyj polecenia /fp:strict.

/fp:strict

/fp:strict Ma zachowanie podobne do /fp:precise, czyli kompilator zachowuje kolejność źródła i zaokrąglanie właściwości kodu zmiennoprzecinkowego podczas generowania i optymalizowania kodu obiektu dla maszyny docelowej i obserwuje standard podczas obsługi wartości specjalnych. Program może również bezpiecznie uzyskiwać dostęp do środowiska zmiennoprzecinkowego lub modyfikować je w czasie wykonywania.

W obszarze /fp:strictkompilator generuje kod, który umożliwia programowi bezpieczne usuwanie maskowania wyjątków zmiennoprzecinkowych, odczyt lub zapis rejestrów stanu zmiennoprzecinkowych lub zmienianie trybów zaokrąglania. Jest zaokrąglany do dokładności kodu źródłowego w czterech określonych punktach podczas obliczania wyrażenia: przy przydziałach, emisjach typu, gdy argumenty zmiennoprzecinkowe są przekazywane do wywołania funkcji, a wywołanie funkcji zwraca wartość zmiennoprzecinkową. Obliczenia pośrednie mogą być wykonywane z dokładnością maszyny. Emisje typu mogą służyć do jawnego zaokrąglić obliczenia pośrednie. Kompilator nie wykonuje żadnych przekształceń algebraicznych w wyrażeniach zmiennoprzecinkowych, takich jak ponowne kojarzenie lub rozkład, chyba że może zagwarantować, że transformacja generuje bitowo identyczny wynik. Wyrażenia obejmujące specjalne wartości (NaN, +infinity, -infinity, -0,0) są przetwarzane zgodnie ze specyfikacjami IEEE-754. Na przykład x != x funkcja ocenia, true czy x jest to NaN. Skurcze zmiennoprzecinkowe nie są generowane w obszarze /fp:strict.

/fp:strict jest obliczanie droższe niż /fp:precise dlatego, że kompilator musi wstawić dodatkowe instrukcje, aby wychwytować wyjątki i zezwolić programom na dostęp do środowiska zmiennoprzecinkowego lub zmodyfikować je w czasie wykonywania. Jeśli kod nie korzysta z tej funkcji, ale wymaga porządkowania i zaokrąglania kodu źródłowego lub opiera się na specjalnych wartościach, użyj polecenia /fp:precise. W przeciwnym razie rozważ użycie elementu /fp:fast, które może generować szybszy i mniejszy kod.

/fp:fast

Opcja /fp:fast umożliwia kompilatorowi zmienianie kolejności, łączenie lub upraszczanie operacji zmiennoprzecinkowych w celu zoptymalizowania kodu zmiennoprzecinkowego pod kątem szybkości i przestrzeni. Kompilator może pominąć zaokrąglanie instrukcji przypisania, emisji typów lub wywołań funkcji. Może zmieniać kolejność operacji lub przekształcać algebraiczne, na przykład za pomocą przepisów asocjacyjnych i dystrybucyjnych. Może zmienić kolejność kodu, nawet jeśli takie przekształcenia powodują zaobserwowane różne działanie zaokrąglania. Ze względu na tę ulepszoną optymalizację wynik niektórych obliczeń zmiennoprzecinkowych może różnić się od tych generowanych przez inne /fp opcje. Wartości specjalne (NaN, +infinity, -infinity, -0.0) mogą nie być propagowane ani zachowywać się ściśle zgodnie ze standardem IEEE-754. Skurcze zmiennoprzecinkowe mogą być generowane w obszarze /fp:fast. Kompilator jest nadal powiązany z podstawową architekturą w obszarze /fp:fast, a więcej optymalizacji może być dostępnych za pomocą /arch opcji .

W obszarze /fp:fastkompilator generuje kod przeznaczony do uruchomienia w domyślnym środowisku zmiennoprzecinkowym i zakłada, że środowisko zmiennoprzecinkowe nie jest dostępne ani modyfikowane w czasie wykonywania. Zakłada to, że kod: pozostawia wyjątki zmiennoprzecinkowe maskowane, nie odczytuje ani nie zapisuje rejestrów stanu zmiennoprzecinkowych i nie zmienia trybów zaokrąglania.

/fp:fast jest przeznaczony dla programów, które nie wymagają ścisłego porządkowania kodu źródłowego i zaokrąglania wyrażeń zmiennoprzecinkowych, i nie polegają na standardowych regułach obsługi wartości specjalnych, takich jak NaN. Jeśli kod zmiennoprzecinkowa wymaga zachowania kolejności kodu źródłowego i zaokrąglania lub opiera się na standardowym zachowaniu wartości specjalnych, użyj polecenia /fp:precise. Jeśli kod uzyskuje dostęp do środowiska zmiennoprzecinkowego lub modyfikuje je w celu zmiany trybów zaokrąglania, usuń maskowanie wyjątków zmiennoprzecinkowych lub sprawdź stan zmiennoprzecinkowy, użyj polecenia /fp:strict.

/fp:except

Opcja /fp:except generuje kod w celu zapewnienia, że wszystkie niemaskowane wyjątki zmiennoprzecinkowe są zgłaszane w dokładnym momencie ich wystąpienia i że nie są wywoływane żadne inne wyjątki zmiennoprzecinkowe. Domyślnie /fp:strict opcja włącza /fp:exceptwartość i /fp:precise nie. Opcja nie jest zgodna /fp:except z /fp:fast. Tę opcję można jawnie wyłączyć za pomocą polecenia /fp:except-.

/fp:except Sama funkcja nie włącza żadnych wyjątków zmiennoprzecinkowych. Jednak wymagane jest, aby programy włączały wyjątki zmiennoprzecinkowe. Aby uzyskać więcej informacji na temat włączania wyjątków zmiennoprzecinkowych, zobacz _controlfp.

Uwagi

Wiele /fp opcji można określić w tym samym wierszu polecenia kompilatora. Jednocześnie może obowiązywać tylko jedna z /fp:strictopcji , /fp:fasti /fp:precise . Jeśli określisz więcej niż jedną z tych opcji w wierszu polecenia, pierwszeństwo ma późniejsza opcja, a kompilator generuje ostrzeżenie. Opcje /fp:strict i /fp:except nie są zgodne z /clr.

Opcja /Za (zgodność ANSI) nie jest zgodna z ./fp

Używanie dyrektyw kompilatora do kontrolowania zachowania zmiennoprzecinkowego

Kompilator udostępnia trzy dyrektywy pragma umożliwiające zastąpienie zachowania zmiennoprzecinkowego określonego w wierszu polecenia: float_control, fenv_accessi fp_contract. Za pomocą tych dyrektyw można kontrolować zachowanie zmiennoprzecinkowe na poziomie funkcji, a nie w ramach funkcji. Te dyrektywy nie odpowiadają bezpośrednio opcjom /fp . W tej tabeli pokazano, jak /fp opcje i dyrektywy pragma mapować się na siebie. Aby uzyskać więcej informacji, zobacz dokumentację poszczególnych opcji i dyrektyw pragma.

Opcja 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

* W wersjach programu Visual Studio przed programem Visual Studio 2022 /fp:precise zachowanie jest domyślnie ustawione na fp_contract(on).

Opcja 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

* W wersjach programu Visual Studio, począwszy od programu Visual Studio 2022, /fp:precise zachowanie jest domyślnie ustawione na fp_contract(off).

Domyślne środowisko zmiennoprzecinkowe

Po zainicjowaniu procesu zostanie ustawione domyślne środowisko zmiennoprzecinkowe. To środowisko maskuje wszystkie wyjątki zmiennoprzecinkowe, ustawia tryb zaokrąglania do najbliższego (), zachowuje wartości podrzędne (FE_TONEARESTdenormalne), używa domyślnej precyzji znaków (mantissa) dla floatwartości , doublei long double , gdzie jest obsługiwana, ustawia kontrolkę nieskończoności na domyślny tryb affine.

Dostęp i modyfikacja środowiska zmiennoprzecinkowego

Środowisko uruchomieniowe programu Microsoft Visual C++ udostępnia kilka funkcji umożliwiających dostęp do środowiska zmiennoprzecinkowego i modyfikowanie go. Należą do nich _controlfpwarianty , _clearfpi _statusfp . Aby zapewnić prawidłowe zachowanie programu, gdy kod uzyskuje dostęp do środowiska zmiennoprzecinkowego lub modyfikuje je, fenv_access należy włączyć za pomocą /fp:strict opcji lub przy użyciu fenv_access pragma, aby te funkcje miały jakikolwiek wpływ. Jeśli fenv_access nie jest włączona, dostęp lub modyfikacja środowiska zmiennoprzecinkowego może spowodować nieoczekiwane zachowanie programu:

  • Kod może nie honorować żądanych zmian w środowisku zmiennoprzecinkowych,

  • Rejestry stanu zmiennoprzecinkowego mogą nie zgłaszać oczekiwanych lub bieżących wyników.

  • Mogą wystąpić nieoczekiwane wyjątki zmiennoprzecinkowe lub oczekiwane wyjątki zmiennoprzecinkowe.

Gdy kod uzyskuje dostęp do środowiska zmiennoprzecinkowego lub modyfikuje go, należy zachować ostrożność podczas łączenia kodu, w którym fenv_access włączono funkcję z kodem, który nie został fenv_access włączony. W kodzie, w którym fenv_access nie jest włączona, kompilator zakłada, że domyślne środowisko zmiennoprzecinkowe platformy działa. Przyjęto również założenie, że stan zmiennoprzecinkowy nie jest uzyskiwany ani modyfikowany. Zalecamy zapisanie i przywrócenie lokalnego środowiska zmiennoprzecinkowego do domyślnego stanu przed przeniesieniem kontrolki do funkcji, która nie została fenv_access włączona. W tym przykładzie pokazano, jak float_control można ustawić i przywrócić pragma:

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

Tryby zaokrąglania zmiennoprzecinkowego

Zarówno w systemach , jak /fp:precise i /fp:fastkompilator generuje kod przeznaczony do uruchomienia w domyślnym środowisku zmiennoprzecinkowa. Przyjęto założenie, że środowisko nie jest dostępne ani modyfikowane w czasie wykonywania. Oznacza to, że kompilator zakłada, że kod nigdy nie maskuje wyjątków zmiennoprzecinkowych, odczytów lub zapisów rejestrów stanu zmiennoprzecinkowych lub zmienia tryby zaokrąglania. Jednak niektóre programy muszą zmienić środowisko zmiennoprzecinkowe. Na przykład ten przykład oblicza granice błędów mnożenia zmiennoprzecinkowego przez zmianę trybów zaokrąglania zmiennoprzecinkowego:

// 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;
}

Ponieważ kompilator zakłada domyślne środowisko zmiennoprzecinkowe w obszarze /fp:fast i /fp:precise, można zignorować wywołania metody _controlfp_s. Na przykład podczas kompilowania przy użyciu architektury /O2 /fp:precise i dla architektury x86 granice nie są obliczane, a przykładowe dane wyjściowe programu:

cLower = -inf
cUpper = -inf

Podczas kompilowania przy użyciu architektury /O2 i /fp:strict dla architektury x86 przykładowy program zwraca następujące dane wyjściowe:

cLower = -inf
cUpper = -3.40282e+38

Wartości specjalne zmiennoprzecinkowe

W obszarze /fp:precise i /fp:strictwyrażenia, które obejmują wartości specjalne (NaN, +infinity, -infinity, -0,0) zachowują się zgodnie ze specyfikacjami IEEE-754. W obszarze /fp:fast, zachowanie tych wartości specjalnych może być niezgodne z ieee-754.

W tym przykładzie pokazano różne zachowanie specjalnych wartości w obszarze /fp:precise, /fp:stricti /fp:fast:

// 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;
}

Podczas kompilowania przy użyciu /O2 /fp:precise architektury x86 lub /O2 /fp:strict dla architektury x86 dane wyjściowe są zgodne ze specyfikacją IEEE-754:

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

Podczas kompilowania przy użyciu polecenia /O2 /fp:fast** dla architektury x86 dane wyjściowe nie są zgodne z ieee-754:

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

Przekształcenia algebraiczne zmiennoprzecinkowe

W obszarze /fp:precise i /fp:strictkompilator nie wykonuje żadnych przekształceń matematycznych, chyba że transformacja ma gwarancję uzyskania nieco identycznego wyniku. Kompilator może wykonać takie przekształcenia w obszarze /fp:fast. Na przykład wyrażenie a * b + a * c w przykładowej funkcji algebraic_transformation może zostać skompilowane w a * (b + c) obszarze /fp:fast. Takie przekształcenia nie są wykonywane w obszarze /fp:precise lub /fp:strict, a kompilator generuje a * b + a * c.

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

Punkty rzutujące jawne zmiennoprzecinkowe

W obszarze /fp:precise i /fp:strictkompilator zaokrągla do precyzji kodu źródłowego w czterech określonych punktach podczas obliczania wyrażenia: przy przypisaniach, emisjach typów, gdy argumenty zmiennoprzecinkowe są przekazywane do wywołania funkcji, a gdy wywołanie funkcji zwraca wartość zmiennoprzecinkową. Emisje typu mogą służyć do jawnego zaokrąglić obliczenia pośrednie. W obszarze /fp:fastkompilator nie generuje jawnych rzutów w tych punktach, aby zagwarantować precyzję kodu źródłowego. W tym przykładzie pokazano zachowanie w różnych /fp opcjach:

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

Podczas kompilowania przy użyciu funkcji /O2 /fp:precise lub /O2 /fp:strictwidać, że jawne rzutowania typów są wstawiane zarówno w emisji typu, jak i w punkcie zwrotnym funkcji w wygenerowanym kodzie architektury x64:

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

W obszarze /O2 /fp:fast wygenerowanego kodu jest uproszczony, ponieważ wszystkie rzuty typu są optymalizowane:

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

Aby ustawić tę opcję kompilatora w środowisku programowania Visual Studio

  1. Otwórz okno dialogowe Strony właściwości projektu. Aby uzyskać szczegółowe informacje, zobacz Set C++ compiler and build properties in Visual Studio (Ustawianie właściwości kompilatora języka C++ i kompilowania w programie Visual Studio).

  2. Wybierz stronę właściwości Właściwości>konfiguracji C/C++>Code Generation.

  3. Zmodyfikuj właściwość Model zmiennoprzecinkowa.

Aby programowo ustawić tę opcję kompilatora

Zobacz też

Opcje kompilatora MSVC
Składnia wiersza polecenia kompilatora MSVC