/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:contract
wartość . 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:precise
kompilator 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:strict
kompilator 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:fast
kompilator 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:except
wartość 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:strict
opcji , /fp:fast
i /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_access
i 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_TONEAREST
denormalne), używa domyślnej precyzji znaków (mantissa) dla float
wartości , double
i 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 _controlfp
warianty , _clearfp
i _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:fast
kompilator 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:strict
wyraż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:strict
i /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:strict
kompilator 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:strict
kompilator 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:fast
kompilator 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:strict
widać, ż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
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).
Wybierz stronę właściwości Właściwości>konfiguracji C/C++>Code Generation.
Zmodyfikuj właściwość Model zmiennoprzecinkowa.
Aby programowo ustawić tę opcję kompilatora
- Zobacz: floatingPointModel.
Zobacz też
Opcje kompilatora MSVC
Składnia wiersza polecenia kompilatora MSVC