/fp
(Especificar comportamiento de punto flotante)
Especifica cómo trata el compilador las expresiones de punto flotante, las optimizaciones y las excepciones. Las opciones /fp
especifican si el código generado permite cambios del entorno de punto flotante en el modo de redondeo, las máscaras de excepción y el comportamiento anormal, y si las comprobaciones de estado de punto flotante devuelven resultados actuales y precisos. Controlan si el compilador genera código que mantiene el orden de las expresiones y las operaciones de origen, y se ajustan al estándar para la propagación NaN. O bien, si en su lugar generan código más eficaz que puede reordenar o combinar operaciones y usar transformaciones algebraicas de simplificación que no están permitidas por el estándar IEEE-754.
Sintaxis
/fp:contract
/fp:except
[-
]
/fp:fast
/fp:precise
/fp:strict
/fp:except
[-
]
/fp:fast
/fp:precise
/fp:strict
Argumentos
/fp:contract
La opción /fp:contract
permite al compilador generar contracciones de punto flotante cuando se especifican las opciones /fp:precise
y /fp:except
. Una contracción es una instrucción automática que combina operaciones de punto flotante, como Fused-Multiply-Add (FMA). FMA, que se define como una operación básica en IEEE-754, no redondea el producto intermedio antes de la suma, por lo que el resultado puede diferir de las operaciones de multiplicación y suma independientes. Puesto que se implementa como una sola instrucción, puede ser más rápida que las instrucciones independientes. La velocidad tiene el costo de los resultados exactos bit a bit y la incapacidad de examinar el valor intermedio.
De manera predeterminada, la opción /fp:fast
habilita /fp:contract
. La opción /fp:contract
no es compatible con /fp:strict
.
La opción /fp:contract
es nueva en Visual Studio 2022.
/fp:precise
De manera predeterminada, el compilador usa el comportamiento de /fp:precise
.
Con /fp:precise
, el compilador conserva el orden de las expresiones de origen y las propiedades de redondeo del código de punto flotante cuando genera y optimiza código de objeto de la máquina de destino. El compilador redondea a la precisión del código fuente en cuatro momentos específicos durante la evaluación de expresiones: en las asignaciones, en las conversiones de tipos, cuando los argumentos de punto flotante se pasan a una llamada de función y cuando una llamada de función devuelve un valor de punto flotante. Los cálculos intermedios se pueden realizar en precisión automática. Las conversiones de tipos se pueden usar para redondear explícitamente cálculos intermedios.
El compilador no realiza transformaciones algebraicas en expresiones de punto flotante, como reasociación o distribución, a menos que pueda garantizar que la transformación genera un resultado idéntico bit a bit. Las expresiones con valores especiales (NaN, +infinity, -infinity, -0.0) se procesan según las especificaciones IEEE-754. Por ejemplo, x != x
se evalúa como true
si x
es NaN. Las contracciones de punto flotante no se generan de manera predeterminada con /fp:precise
. Este comportamiento es nuevo en Visual Studio 2022. Las versiones anteriores del compilador podían generar contracciones de manera predeterminada con /fp:precise
.
El compilador no realiza transformaciones algebraicas en expresiones de punto flotante, como reasociación o distribución, a menos que pueda garantizar que la transformación genera un resultado idéntico bit a bit. Las expresiones con valores especiales (NaN, +infinity, -infinity, -0.0) se procesan según las especificaciones IEEE-754. Por ejemplo, x != x
se evalúa como true
si x
es NaN. Las contracciones de punto flotante se pueden generar con /fp:precise
.
El compilador genera código destinado a ejecutarse en el entorno de punto flotante predeterminado. También da por hecho que no se accede al entorno de punto flotante ni se modifica en tiempo de ejecución. Es decir, da por hecho que el código: deja enmascaradas las excepciones de punto flotante, no lee ni escribe registros de estado de punto flotante y no cambia los modos de redondeo.
Si el código de punto flotante no depende del orden de las operaciones y expresiones de las instrucciones de punto flotante (por ejemplo, si no le importa si a * b + a * c
se calcula como (b + c) * a
, o 2 * a
como a + a
), considere la opción /fp:fast
, que puede producir código más rápido y eficaz. Si el código depende del orden de las operaciones y las expresiones, y accede o modifica el entorno de punto flotante (por ejemplo, para cambiar los modos de redondeo o para interceptar excepciones de punto flotante), use /fp:strict
.
/fp:strict
/fp:strict
tiene un comportamiento similar a /fp:precise
, es decir, el compilador conserva el orden de origen y las propiedades de redondeo del código de punto flotante cuando genera y optimiza código de objeto de la máquina de destino, y cumple el estándar cuando controla valores especiales. El programa también puede acceder de forma segura al entorno de punto flotante o modificarlo en tiempo de ejecución.
Con /fp:strict
, el compilador genera código que permite al programa desenmascarar de forma segura excepciones de punto flotante, leer o escribir registros de estado de punto flotante, o cambiar modos de redondeo. Redondea a la precisión del código fuente en cuatro momentos específicos durante la evaluación de expresiones: en las asignaciones, en las conversiones de tipos, cuando los argumentos de punto flotante se pasan a una llamada de función y cuando una llamada de función devuelve un valor de punto flotante. Los cálculos intermedios se pueden realizar en precisión automática. Las conversiones de tipos se pueden usar para redondear explícitamente cálculos intermedios. El compilador no realiza transformaciones algebraicas en expresiones de punto flotante, como reasociación o distribución, a menos que pueda garantizar que la transformación genera un resultado idéntico bit a bit. Las expresiones con valores especiales (NaN, +infinity, -infinity, -0.0) se procesan según las especificaciones IEEE-754. Por ejemplo, x != x
se evalúa como true
si x
es NaN. Las contracciones de punto flotante no se generan con /fp:strict
.
Desde el punto de vista del cálculo, /fp:strict
es más costoso que /fp:precise
, ya que el compilador debe insertar instrucciones adicionales para interceptar excepciones y permitir que los programas accedan al entorno de punto flotante o lo modifiquen en tiempo de ejecución. Si el código no usa esta capacidad, pero requiere orden y redondeo del código fuente, o se basa en valores especiales, use /fp:precise
. De lo contrario, considere la posibilidad de usar /fp:fast
, que puede generar código más rápido y de menor tamaño.
/fp:fast
La opción /fp:fast
permite al compilador reordenar, combinar o simplificar las operaciones de punto flotante a fin de optimizar el código de punto flotante para lograr velocidad y espacio. El compilador puede omitir el redondeo en instrucciones de asignación, conversiones de tipos o llamadas de función. Puede reordenar las operaciones o realizar transformaciones algebraicas, por ejemplo, mediante el uso de leyes asociativas y distributivas. Puede reordenar el código aunque estas transformaciones den lugar a un comportamiento de redondeo notablemente diferente. Debido a esta optimización mejorada, el resultado de algunos cálculos de punto flotante puede diferir de los generados por otras opciones /fp
. Es posible que los valores especiales (NaN, +infinity, -infinity, -0.0) no se propaguen ni se comporten estrictamente conforme al estándar IEEE-754. Las contracciones de punto flotante se pueden generar con /fp:fast
. El compilador sigue enlazado por la arquitectura subyacente con /fp:fast
, y es posible que haya más optimizaciones disponibles por medio de la opción /arch
.
Con /fp:fast
, el compilador genera código destinado a ejecutarse en el entorno de punto flotante predeterminado y da por hecho que no se accede al entorno de punto flotante ni se modifica en tiempo de ejecución. Es decir, da por hecho que el código: deja enmascaradas las excepciones de punto flotante, no lee ni escribe registros de estado de punto flotante y no cambia los modos de redondeo.
/fp:fast
está pensado para programas que no requieren un orden estricto del código fuente ni el redondeo de las expresiones de punto flotante, y que no se basan en las reglas estándar para controlar valores especiales, como NaN
. Si el código de punto flotante exige la conservación del orden y el redondeo del código fuente, o se basa en el comportamiento estándar de los valores especiales, use /fp:precise
. Si el código accede al entorno de punto flotante o lo modifica para cambiar los modos de redondeo, desenmascarar excepciones de punto flotante o comprobar el estado de punto flotante, use /fp:strict
.
/fp:except
La opción /fp:except
genera código que garantiza que las excepciones de punto flotante desenmascaradas se generen en el momento exacto en que se producen, y que no se generen otras excepciones de punto flotante. De manera predeterminada, la opción /fp:strict
habilita /fp:except
, y /fp:precise
no. La opción /fp:except
no es compatible con /fp:fast
. La opción se puede deshabilitar explícitamente mediante /fp:except-
.
Por sí solo, /fp:except
no habilita ninguna excepción de punto flotante. Pero es necesario para que los programas habiliten excepciones de punto flotante. Para obtener más información sobre cómo habilitar excepciones de punto flotante, vea _controlfp
.
Comentarios
En la misma línea de comandos del compilador se pueden especificar varias opciones /fp
. Solo una de las opciones /fp:strict
, /fp:fast
y /fp:precise
puede aplicarse cada vez. Si se especifican más de una de estas opciones en la línea de comandos, la última tiene prioridad y el compilador genera una advertencia. Las opciones /fp:strict
y /fp:except
no son compatibles con /clr
.
La opción /Za
(compatibilidad ANSI) no es compatible con /fp
.
Uso de directivas del compilador para controlar el comportamiento de punto flotante
El compilador proporciona tres directivas pragma para invalidar el comportamiento de punto flotante especificado en la línea de comandos: float_control
, fenv_access
y fp_contract
. Puede usar estas directivas para controlar el comportamiento de punto flotante en el nivel de función, no dentro de una función. Estas directivas no se corresponden directamente con las opciones /fp
. En esta tabla se muestra cómo se asignan las opciones /fp
y las directivas pragma entre sí. Para obtener más información, vea la documentación de las opciones y las directivas pragma individuales.
Opción | 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 |
* En versiones de Visual Studio anteriores a Visual Studio 2022, el comportamiento predeterminado de /fp:precise
es fp_contract(on)
.
Opción | 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 |
* En versiones de Visual Studio a partir de Visual Studio 2022, el comportamiento predeterminado de /fp:precise
es fp_contract(off)
.
Entorno de punto flotante predeterminado
Cuando se inicializa un proceso, se establece el entorno de punto flotante predeterminado. Este entorno enmascara todas las excepciones de punto flotante, establece el modo de redondeo en el redondeo al más cercano (FE_TONEAREST
), conserva los valores anormales (no normales), usa la precisión predeterminada de significando (mantisa) para los valores float
, double
y long double
donde se admite, establece el control de infinito en el modo afín predeterminado.
Acceso al entorno de punto flotante y modificación
El runtime de Microsoft Visual C++ proporciona varias funciones para acceder al entorno de punto flotante y modificarlo. Estas incluyen _controlfp
, _clearfp
y _statusfp
, y sus variantes. Para garantizar el comportamiento correcto del programa cuando el código accede al entorno de punto flotante o lo modifica, fenv_access
debe estar habilitado, ya sea mediante la opción /fp:strict
o por medio del uso de la pragma fenv_access
, para que estas funciones tengan algún efecto. Si fenv_access
no está habilitado, el acceso al entorno de punto flotante o su modificación pueden dar lugar a un comportamiento inesperado del programa:
Es posible que el código no respete los cambios solicitados en el entorno de punto flotante,
Es posible que los registros de estado de punto flotante no notifiquen los resultados esperados o actuales,
Es posible que se produzcan excepciones de punto flotante inesperadas o que no se produzcan excepciones de punto flotante esperadas.
Si el código accede al entorno de punto flotante o lo modifica, debe tener cuidado al combinar código donde fenv_access
esté habilitado con código que no tenga fenv_access
habilitado. En el código donde fenv_access
no está habilitado, el compilador da por hecho que el entorno de punto flotante predeterminado de la plataforma está aplicado. También da por hecho que no se accede al estado de punto flotante ni se modifica. Se recomienda guardar y restaurar el entorno de punto flotante local a su estado predeterminado antes de que el control se transfiera a una función que no tenga fenv_access
habilitado. En este ejemplo se muestra cómo se puede establecer y restaurar la pragma float_control
:
#pragma float_control(precise, on, push)
// Code that uses /fp:strict mode
#pragma float_control(pop)
Modos de redondeo de punto flotante
Con /fp:precise
y /fp:fast
, el compilador genera código destinado a ejecutarse en el entorno de punto flotante predeterminado. Da por hecho que no se accede al entorno ni se modifica en tiempo de ejecución. Es decir, el compilador da por hecho que el código nunca desenmascara excepciones de punto flotante, lee o escribe registros de estado de punto flotante ni cambia modos de redondeo. Pero algunos programas deben modificar el entorno de punto flotante. Por ejemplo, en este ejemplo se calculan los límites de error de una multiplicación de punto flotante por medio de la modificación de los modos de redondeo de punto flotante:
// 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;
}
Dado que el compilador da por hecho el entorno de punto flotante predeterminado con /fp:fast
y /fp:precise
, es libre de omitir las llamadas a _controlfp_s
. Por ejemplo, cuando se compila mediante /O2
y /fp:precise
en la arquitectura x86, los límites no se calculan y el programa de ejemplo genera:
cLower = -inf
cUpper = -inf
Cuando se compila mediante /O2
y /fp:strict
en la arquitectura x86, el programa de ejemplo genera:
cLower = -inf
cUpper = -3.40282e+38
Valores especiales de punto flotante
Con /fp:precise
y /fp:strict
, las expresiones con valores especiales (NaN, +infinity, -infinity, -0.0) se comportan según las especificaciones IEEE-754. Con /fp:fast
, el comportamiento de estos valores especiales puede ser incoherente con IEEE-754.
En este ejemplo se muestra el diferente comportamiento de los valores especiales con /fp:precise
, /fp:strict
y /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;
}
Cuando se compila mediante /O2 /fp:precise
o /O2 /fp:strict
en la arquitectura x86, las salidas son coherentes con la especificación IEEE-754:
INFINITY == INFINITY : 1
NAN == NAN : 0
INFINITY - INFINITY : -nan(ind)
NAN - NAN : nan
std::signbit(-0.0/-INFINITY): 0
Cuando se compila mediante /O2 /fp:fast
** en la arquitectura x86, las salidas no son coherentes con IEEE-754:
INFINITY == INFINITY : 1
NAN == NAN : 1
INFINITY - INFINITY : 0.000000
NAN - NAN : 0.000000
std::signbit(-0.0/-INFINITY): 0
Transformaciones algebraicas de punto flotante
Con /fp:precise
y /fp:strict
, el compilador no realiza ninguna transformación matemática a menos que se garantice que esta va a generar un resultado idéntico bit a bit. El compilador puede realizar estas transformaciones con /fp:fast
. Por ejemplo, la expresión a * b + a * c
de la función algebraic_transformation
de ejemplo se puede compilar en a * (b + c)
, con /fp:fast
. Estas transformaciones no se realizan con /fp:precise
o /fp:strict
, y el compilador genera a * b + a * c
.
float algebraic_transformation (float a, float b, float c)
{
return a * b + a * c;
}
Puntos de conversión explícitos de punto flotante
Con /fp:precise
y /fp:strict
, compilador redondea a la precisión del código fuente en cuatro momentos específicos durante la evaluación de expresiones: en las asignaciones, en las conversiones de tipos, cuando los argumentos de punto flotante se pasan a una llamada de función y cuando una llamada de función devuelve un valor de punto flotante. Las conversiones de tipos se pueden usar para redondear explícitamente cálculos intermedios. Con /fp:fast
, el compilador no genera conversiones explícitas en estos momentos para garantizar la precisión del código fuente. En este ejemplo se muestra el comportamiento con distintas opciones /fp
:
float casting(float a, float b)
{
return 5.0*((double)(a+b));
}
Cuando se compila mediante /O2 /fp:precise
o /O2 /fp:strict
, puede ver que las conversiones de tipos explícitas se insertan tanto en el momento de la conversión de tipos como en el de devolución de la función en el código generado en la arquitectura x64:
addss xmm0, xmm1
cvtss2sd xmm0, xmm0
mulsd xmm0, QWORD PTR __real@4014000000000000
cvtsd2ss xmm0, xmm0
ret 0
Con /O2 /fp:fast
, el código generado se simplifica, porque todas las conversiones de tipos están optimizadas:
addss xmm0, xmm1
mulss xmm0, DWORD PTR __real@40a00000
ret 0
Para establecer esta opción del compilador en el entorno de desarrollo de Visual Studio
Abra el cuadro de diálogo Páginas de propiedades del proyecto. Para más información, vea Establecimiento del compilador de C++ y de propiedades de compilación en Visual Studio.
Seleccione la página de propiedades Propiedades de configuración>C/C++>Generación de código.
Modifique la propiedad Modelo de punto flotante.
Para establecer esta opción del compilador mediante programación
- Vea floatingPointModel.
Consulte también
Opciones del compilador de MSVC
Sintaxis de línea de comandos del compilador de MSVC