/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

  1. 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.

  2. Seleccione la página de propiedades Propiedades de configuración>C/C++>Generación de código.

  3. Modifique la propiedad Modelo de punto flotante.

Para establecer esta opción del compilador mediante programación

Consulte también

Opciones del compilador de MSVC
Sintaxis de línea de comandos del compilador de MSVC