Partilhar via


/fp (Especificar o comportamento de ponto flutuante)

Especifica como o compilador trata expressões, otimizações e exceções de ponto flutuante. As opções /fp especificam se o código gerado permite alterações do ambiente de ponto flutuante no modo de arredondamento, nas máscaras de exceção e no comportamento subnormal e se as verificações de status de ponto flutuante retornam resultados atuais e precisos. Ela controla se o compilador gera código que mantém a ordem da expressão e operação de origem e se mantém conformidade com o padrão de propagação NaN. Ou se gera um código mais eficiente que possa reordenar ou combinar operações e usar transformações algébricas de simplificação que não são permitidas pelo padrão IEEE-754.

Sintaxe

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

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

Argumentos

/fp:contract

A opção /fp:contract permite que o compilador gere contrações de ponto flutuante quando você especifica as opções /fp:precise e /fp:except. Uma contração é uma instrução do computador que combina operações de ponto flutuante, como FMA (Fused-Multiply-Add). A FMA, definida como uma operação básica pelo IEEE-754, não arredonda o produto intermediário antes da adição, portanto, o resultado pode diferir das operações de multiplicação e adição separadas. Como é implementada como uma única instrução, ela pode ser mais rápida do que instruções separadas. A velocidade traz o custo de resultados exatos bit a bit e uma incapacidade de examinar o valor intermediário.

Por padrão, a opção /fp:fast habilita a /fp:contract. A opção /fp:contract não é compatível com /fp:strict.

A opção /fp:contract é nova no Visual Studio 2022.

/fp:precise

Por padrão, o compilador usa o comportamento /fp:precise.

No /fp:precise, o compilador preserva as propriedades de ordenação e arredondamento da expressão de origem do código de ponto flutuante quando ele gera e otimiza o código de objeto para o computador de destino. O compilador arredonda conforme a precisão do código-fonte em quatro pontos específicos durante a avaliação da expressão: em atribuições, em typecasts, quando argumentos de ponto flutuante são passados para uma chamada de função e quando uma chamada de função retorna um valor de ponto flutuante. Cálculos intermediários podem ser executados com a precisão do computador. Os typecasts podem ser usados para arredondar explicitamente os cálculos intermediários.

O compilador não executa transformações algébricas em expressões de ponto flutuante, como reassociação ou distribuição, a menos que possa garantir que a transformação produza um resultado idêntico bit a bit. Expressões que envolvem valores especiais (NaN, +infinito, -infinito, -0,0) são processadas de acordo com as especificações do IEEE-754. Por exemplo, x != x é avaliada como true se x for NaN. As contrações de ponto flutuante não são geradas por padrão no /fp:precise. Esse comportamento é novo no Visual Studio 2022. As versões anteriores do compilador podiam gerar contrações por padrão no /fp:precise.

O compilador não executa transformações algébricas em expressões de ponto flutuante, como reassociação ou distribuição, a menos que possa garantir que a transformação produza um resultado idêntico bit a bit. Expressões que envolvem valores especiais (NaN, +infinito, -infinito, -0,0) são processadas de acordo com as especificações do IEEE-754. Por exemplo, x != x é avaliada como true se x for NaN. As contrações de ponto flutuante podem ser geradas no /fp:precise.

O compilador gera o código destinado a ser executado no ambiente de ponto flutuante padrão. Ele também pressupõe que o ambiente de ponto flutuante não seja acessado nem modificado no runtime. Ou seja, ele pressupõe que o código: deixe exceções de ponto flutuante mascaradas, não leia nem grave registros de status de ponto flutuante e não altere os modos de arredondamento.

Se o seu código de ponto flutuante não depender da ordem das operações e expressões nas instruções de ponto flutuante (por exemplo, se você não se importar se a * b + a * c é computado como (b + c) * a ou 2 * a como a + a), considere a opção /fp:fast, que pode produzir um código mais rápido e eficiente. Se o seu código depender da ordem de operações e expressões e acessar ou alterar o ambiente de ponto flutuante (por exemplo, para alterar modos de arredondamento ou para capturar exceções de ponto flutuante), use /fp:strict.

/fp:strict

O /fp:strict tem um comportamento semelhante ao /fp:precise, ou seja, o compilador preserva as propriedades de ordenação e arredondamento da origem do código de ponto flutuante quando gera e otimiza o código do objeto para o computador de destino e observa o padrão ao manipular valores especiais. O programa também pode acessar ou modificar com segurança o ambiente de ponto flutuante em runtime.

No /fp:strict, o compilador gera um código que permite ao programa desmascarar com segurança exceções de ponto flutuante, ler ou gravar registros de status de ponto flutuante ou alterar modos de arredondamento. Ele arredonda conforme a precisão do código-fonte em quatro pontos específicos durante a avaliação da expressão: em atribuições, em typecasts, quando argumentos de ponto flutuante são passados para uma chamada de função e quando uma chamada de função retorna um valor de ponto flutuante. Cálculos intermediários podem ser executados com a precisão do computador. Os typecasts podem ser usados para arredondar explicitamente os cálculos intermediários. O compilador não faz nenhuma transformação algébrica em expressões de ponto flutuante, como reassociação ou distribuição, a menos que possa garantir que a transformação produza um resultado idêntico bit a bit. Expressões que envolvem valores especiais (NaN, +infinito, -infinito, -0,0) são processadas de acordo com as especificações do IEEE-754. Por exemplo, x != x é avaliada como true se x for NaN. As contrações de ponto flutuante não são geradas no /fp:strict.

O /fp:strict é computacionalmente mais caro do que /fp:precise, porque o compilador deve inserir instruções adicionais para capturar exceções e permitir que os programas acessem ou modifiquem o ambiente de ponto flutuante em runtime. Se o seu código não usa essa funcionalidade, mas exige a ordenação e o arredondamento do código-fonte ou depende de valores especiais, use /fp:precise. Caso contrário, considere usar /fp:fast, que pode produzir código mais rápido e menor.

/fp:fast

A opção /fp:fast permite que o compilador reordene, combine ou simplifique as operações de ponto flutuante para otimizar a velocidade e o espaço do código de ponto flutuante. O compilador pode omitir o arredondamento em instruções de atribuição, typecasts ou chamadas de função. Ele pode reordenar operações ou fazer transformações algébricas, por exemplo, usando leis associativas e distributivas. Ele pode reordenar o código mesmo que essas transformações resultem em um comportamento de arredondamento perceptivelmente diferente. Devido a essa otimização aprimorada, o resultado de alguns cálculos de ponto flutuante pode ser diferente daqueles produzidos por outras opções /fp. Valores especiais (NaN, +infinito, -infinito, -0,0) podem não ser propagados nem se comportar estritamente de acordo com o padrão IEEE-754. As contrações de ponto flutuante podem ser geradas no /fp:fast. O compilador ainda está associado à arquitetura subjacente no /fp:fast e mais otimizações podem estar disponíveis por meio do uso da opção /arch.

No /fp:fast, o compilador gera o código destinado a ser executado no ambiente de ponto flutuante padrão e pressupõe que o ambiente de ponto flutuante não seja acessado nem modificado em runtime. Ou seja, ele pressupõe que o código: deixe exceções de ponto flutuante mascaradas, não leia nem grave registros de status de ponto flutuante e não altere os modos de arredondamento.

O /fp:fast destina-se a programas que não exigem ordenação estrita do código-fonte e arredondamento de expressões de ponto flutuante e que não dependem das regras padrão para lidar com valores especiais, como NaN. Se o seu código de ponto flutuante exigir a preservação da ordenação e arredondamento do código-fonte ou se depender do comportamento padrão de valores especiais, use /fp:precise. Se o código acessa ou modifica o ambiente de ponto flutuante para alterar modos de arredondamento, desmascarar exceções de ponto flutuante ou verificar o status de ponto flutuante, use /fp:strict.

/fp:except

A opção /fp:except gera o código para garantir que as exceções de ponto flutuante desmascaradas sejam geradas no ponto exato em que ocorrem e que nenhuma outra exceção de ponto flutuante seja gerada. Por padrão, a opção /fp:strict habilita /fp:except e a /fp:precise não. A opção /fp:except não é compatível com /fp:fast. A opção pode ser explicitamente desabilitada pelo uso de /fp:except-.

Por si só, a /fp:except não habilita nenhuma exceção de ponto flutuante. No entanto, é necessário que os programas habilitem exceções de ponto flutuante. Para obter mais informações sobre como habilitar exceções de ponto flutuante, confira _controlfp.

Comentários

Várias opções /fp podem ser especificadas na mesma linha de comando do compilador. Somente uma das opções /fp:strict, /fp:fast e /fp:precise pode estar em vigor a cada vez. Se você especificar mais de uma dessas opções na linha de comando, a opção posterior terá precedência e o compilador gerará um aviso. As opções /fp:strict e /fp:except não são compatíveis com /clr.

A opção /Za (compatibilidade com ANSI) não é compatível com /fp.

Usar diretivas do compilador para controlar o comportamento de ponto flutuante

O compilador fornece três diretivas de pragma para substituir o comportamento de ponto flutuante especificado na linha de comando: float_control, fenv_access e fp_contract. Você pode usar essas diretivas para controlar o comportamento de ponto flutuante no nível da função, não dentro de uma função. Essas diretivas não correspondem diretamente às opções /fp. Esta tabela mostra como as opções /fp e as diretivas de pragma são mapeadas entre si. Para obter mais informações, veja a documentação das opções individuais e diretivas de pragma.

Opção 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

* Em versões do Visual Studio anteriores ao Visual Studio 2022, o comportamento /fp:precise adotava fp_contract(on) como padrão.

Opção 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

* Em versões do Visual Studio posteriores ao Visual Studio 2022, o comportamento /fp:precise adota fp_contract(off) como padrão.

O ambiente de ponto flutuante padrão

Quando um processo é inicializado, o ambiente de ponto flutuante padrão é definido. Esse ambiente mascara todas as exceções de ponto flutuante, define o modo de arredondamento como arredondar para o mais próximo (FE_TONEAREST), preserva valores subnormais (denormal), usa a precisão padrão de significando (mantissa) para valores float, double e long double e quando houver suporte, define o controle de infinito com o modo de afinidade padrão.

Acesso e modificação do ambiente de ponto flutuante

O runtime do Microsoft Visual C++ fornece várias funções para acessar e modificar o ambiente de ponto flutuante. Elas incluem _controlfp, _clearfp e _statusfp e suas variantes. Para garantir o comportamento correto do programa quando o código acessa ou modifica o ambiente de ponto flutuante, o fenv_access deve ser habilitado, seja pela opção /fp:strict ou pelo uso do pragma fenv_access, para que essas funções tenham algum efeito. Se o fenv_access não estiver habilitado, o acesso ou a modificação do ambiente de ponto flutuante poderá resultar em um comportamento inesperado do programa:

  • O código poderá não respeitar as alterações solicitadas no ambiente de ponto flutuante,

  • Os registradores de status de ponto flutuante poderão não relatar os resultados esperados ou atuais,

  • Exceções inesperadas de ponto flutuante poderão ocorrer ou exceções de ponto flutuante esperadas poderão não ocorrer.

Quando o seu código acessa ou modifica o ambiente de ponto flutuante, você deve ter cuidado ao combinar o código em que fenv_access está habilitado com o código que não tem o fenv_access habilitado. No código em que fenv_access não está habilitado, o compilador pressupõe que o ambiente de ponto flutuante padrão da plataforma esteja em vigor. Ele também pressupõe que o status do ponto flutuante não será acessado nem modificado. Recomendamos que você salve e restaure o ambiente de ponto flutuante local de acordo com o estado padrão antes que o controle seja transferido para uma função que não tenha fenv_access habilitado. Este exemplo demonstra como o pragma float_control pode ser definido e restaurado:

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

Modos de arredondamento de ponto flutuante

No /fp:precise e no /fp:fast, o compilador gera o código destinado a ser executado no ambiente de ponto flutuante padrão. Ele pressupõe que o ambiente não será acessado nem modificado em runtime. Ou seja, o compilador pressupõe que o código nunca desmascara exceções de ponto flutuante, não lê nem grava registros de status de ponto flutuante nem altera os modos de arredondamento. No entanto, alguns programas precisam alterar o ambiente de ponto flutuante. Por exemplo, este exemplo calcula os limites de erro de uma multiplicação de ponto flutuante alterando os modos de arredondamento de ponto flutuante:

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

Como o compilador assume o ambiente de ponto flutuante padrão em /fp:fast e /fp:precise, ele fica livre para ignorar as chamadas ao _controlfp_s. Por exemplo, quando compilado usando /O2 e /fp:precise para a arquitetura x86, os limites não são calculados e o programa de exemplo gera:

cLower = -inf
cUpper = -inf

Quando compilado usando /O2 e /fp:strict para a arquitetura x86, o programa de exemplo gera:

cLower = -inf
cUpper = -3.40282e+38

Valores especiais de ponto flutuante

Em /fp:precise e /fp:strict, expressões que envolvem valores especiais (NaN, +infinito, -infinito, -0,0) se comportam de acordo com as especificações do IEEE-754. No /fp:fast, o comportamento desses valores especiais pode ser inconsistente com o IEEE-754.

Este exemplo demonstra o comportamento diferente dos valores especiais em /fp:precise, /fp:strict e /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;
}

Quando compilado usando /O2 /fp:precise ou /O2 /fp:strict para a arquitetura x86, as saídas são consistentes com a especificação IEEE-754:

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

Quando compilado usando /O2 /fp:fast** para a arquitetura x86, as saídas não são consistentes com a IEEE-754:

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

Transformações algébricas de ponto flutuante

Em /fp:precise e /fp:strict, o compilador não faz nenhuma transformação matemática, a menos que a transformação seja garantida para produzir um resultado idêntico bit a bit. O compilador pode fazer essas transformações no /fp:fast. Por exemplo, a expressão a * b + a * c na função de exemplo algebraic_transformation pode ser compilada em a * (b + c) no /fp:fast. Essas transformações não são feitas no /fp:precise nem no /fp:strict e o compilador gera a * b + a * c.

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

Pontos de conversão explícitos de ponto flutuante

No /fp:precise e no /fp:strict, o compilador arredonda conforme a precisão do código-fonte em quatro pontos específicos durante a avaliação da expressão: em atribuições, em typecasts, quando argumentos de ponto flutuante são passados para uma chamada de função e quando uma chamada de função retorna um valor de ponto flutuante. Os typecasts podem ser usados para arredondar explicitamente os cálculos intermediários. No /fp:fast, o compilador não gera conversões explícitas nesses pontos para garantir a precisão do código-fonte. Este exemplo demonstra o comportamento em diferentes opções de /fp:

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

Quando compilado usando /O2 /fp:precise ou /O2 /fp:strict, você vê que as conversões de tipo explícitas são inseridas no typecast e no ponto de retorno da função no código gerado para a arquitetura x64:

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

No /O2 /fp:fast, código gerado é simplificado, pois todas as conversões de tipo tem a otimização removida:

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

Para definir esta opção do compilador no ambiente de desenvolvimento do Visual Studio

  1. Abra a caixa de diálogo Páginas de Propriedades do projeto. Para obter detalhes, confira Definir as propriedades de build e do compilador do C++ no Visual Studio.

  2. Selecione a página de propriedades Propriedades da Configuração>C/C++>Geração de Código.

  3. Modifique a propriedade Modelo de Ponto Flutuante.

Para definir essa opção do compilador via programação

Confira também

Opções do compilador MSVC
Sintaxe da linha de comando do compilador MSVC