Поделиться через


/fp (Укажите поведение с плавающей запятой)

Указывает, как компилятор обрабатывает выражения с плавающей запятой, оптимизации и исключения. Параметры /fp указывают, может ли созданный код изменять среду с плавающей запятой в режим округления, маски исключений и ненормальное поведение, а также возвращать ли плавающая точка проверки состояния возвращать текущие, точные результаты. Он определяет, создает ли компилятор код, который поддерживает порядок исходных операций и выражений и соответствует стандарту распространения NaN. Или, если вместо этого создается более эффективный код, который может изменить порядок операций или объединить операции и использовать упрощение алгебраических преобразований, которые не допускаются стандартом IEEE-754.

Синтаксис

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

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

Аргументы

/fp:contract

Параметр /fp:contract позволяет компилятору создавать контракты с плавающей запятой при указании /fp:precise и /fp:except параметров. Сокращение — это машинная инструкция, которая объединяет операции с плавающей запятой, такие как fused-Умножение -Add (FMA). FMA, определяемая как базовая операция IEEE-754, не округляет промежуточный продукт до добавления, поэтому результат может отличаться от отдельных операций умножения и сложения. Так как она реализована в виде одной инструкции, она может быть быстрее, чем отдельные инструкции. Скорость приходит побитовой точной результаты и неспособность проверить промежуточное значение.

По умолчанию параметр /fp:fast включает /fp:contract. Параметр /fp:contract не совместим с /fp:strict.

Этот /fp:contract параметр доступен в Visual Studio 2022.

/fp:precise

По умолчанию компилятор использует /fp:precise поведение.

В разделе /fp:preciseкомпилятор сохраняет упорядочение и округление свойств исходного выражения кода с плавающей запятой при создании и оптимизации кода объекта для целевого компьютера. Компилятор округляет точность исходного кода в четырех определенных точках во время вычисления выражений: при назначении, рассылке типов, при передаче аргументов с плавающей запятой в вызов функции и при возврате вызова функции значение с плавающей запятой. Промежуточные вычисления могут выполняться с точностью компьютера. Методы typecasts можно использовать для явного округления промежуточных вычислений.

Компилятор не выполняет преобразования алгебраических значений для выражений с плавающей запятой, таких как повторное связывание или распределение, если преобразование не гарантирует, что преобразование выдает побитовый результат. Выражения, включающие специальные значения (NaN, +бесконечность, -бесконечность, -0,0) обрабатываются в соответствии со спецификациями IEEE-754. Например, вычисляется значение x != x true x NaN. Контракты с плавающей запятой не создаются по умолчанию /fp:precise. Это новое поведение в Visual Studio 2022. Предыдущие версии компилятора могут создавать контракты по умолчанию в /fp:preciseразделе .

Компилятор не выполняет преобразования алгебраических значений для выражений с плавающей запятой, таких как повторное связывание или распределение, если преобразование не гарантирует, что преобразование выдает побитовый результат. Выражения, включающие специальные значения (NaN, +бесконечность, -бесконечность, -0,0) обрабатываются в соответствии со спецификациями IEEE-754. Например, вычисляется значение x != x true x NaN. Контракты с плавающей запятой могут быть созданы в /fp:preciseразделе .

Компилятор создает код, предназначенный для запуска в среде с плавающей запятой по умолчанию. Кроме того, предполагается, что среда с плавающей запятой не обращается к среде выполнения или не изменяется. То есть предполагается, что код: оставляет маскированные исключения с плавающей запятой, не считывает или не записывает регистры состояния с плавающей запятой и не изменяет режимы округления.

Если код с плавающей запятой не зависит от порядка операций и выражений в операторах с плавающей запятой (например, если вы не заботитесь о том, вычисляется ли a * b + a * c как или 2 * a какa + a), рассмотрите /fp:fast вариант, который может создавать более быстрый, более эффективный (b + c) * a код. Если код зависит от порядка операций и выражений, а также обращается к среде с плавающей запятой (например, для изменения режимов округления или перехвата исключений с плавающей запятой), используйте ./fp:strict

/fp:strict

/fp:strict имеет поведение, аналогичное /fp:precise, то есть компилятор сохраняет исходный порядок и округление свойств кода с плавающей запятой при создании и оптимизации кода объекта для целевого компьютера и наблюдает стандарт при обработке специальных значений. Программа также может безопасно получить доступ к среде с плавающей запятой или изменить ее во время выполнения.

В разделе /fp:strictкомпилятор создает код, позволяющий программе безопасно распаковывать исключения с плавающей запятой, считывать или записывать регистры состояния с плавающей запятой или изменять режимы округления. Он округляется до точности исходного кода в четырех определенных точках во время вычисления выражений: при назначении, типах, при передаче аргументов с плавающей запятой в вызов функции и при возврате вызова функции значение с плавающей запятой. Промежуточные вычисления могут выполняться с точностью компьютера. Методы typecasts можно использовать для явного округления промежуточных вычислений. Компилятор не делает никаких алгебраических преобразований в выражениях с плавающей запятой, таких как повторное связывание или распределение, если не гарантируется, что преобразование создает побитовую идентичную результат. Выражения, включающие специальные значения (NaN, +бесконечность, -бесконечность, -0,0) обрабатываются в соответствии со спецификациями IEEE-754. Например, вычисляется значение x != x true x NaN. Контракты с плавающей запятой не создаются в /fp:strictразделе.

/fp:strict вычисляется дороже, чем /fp:precise из-за того, что компилятор должен вставить дополнительные инструкции для перехвата исключений и разрешить программам доступ к среде с плавающей запятой или изменять ее во время выполнения. Если код не использует эту возможность, но требует упорядочения и округления исходного кода или использует специальные значения, используйте /fp:precise. В противном случае рассмотрите возможность использования /fp:fast, который может создавать более быстрый и меньший код.

/fp:fast

Этот /fp:fast параметр позволяет компилятору переупорядочение, объединение или упрощение операций с плавающей запятой для оптимизации кода с плавающей запятой для скорости и пространства. Компилятор может опустить округление при операторах назначения, типах или вызовах функций. Он может изменять порядок операций или создавать алгебраические преобразования, например с помощью ассоциативных и дистрибутивных законов. Он может изменить порядок кода, даже если такие преобразования приводят к заметно другому циклического поведения. Из-за этой расширенной оптимизации результат некоторых вычислений с плавающей запятой может отличаться от тех, которые создаются другими /fp вариантами. Специальные значения (NaN, +бесконечность, -бесконечность, -0,0) могут не распространяться или вести себя строго в соответствии со стандартом IEEE-754. Контракты с плавающей запятой могут быть созданы в /fp:fastразделе . Компилятор по-прежнему привязан к базовой архитектуре /fp:fast, и дополнительные оптимизации могут быть доступны с помощью /arch параметра.

В разделе /fp:fastкомпилятор создает код, предназначенный для выполнения в среде с плавающей запятой по умолчанию, и предполагает, что среда с плавающей запятой не обращается или не изменяется во время выполнения. То есть предполагается, что код: оставляет маскированные исключения с плавающей запятой, не считывает или не записывает регистры состояния с плавающей запятой и не изменяет режимы округления.

/fp:fast предназначен для программ, которые не требуют строгого порядка исходного кода и округления выражений с плавающей запятой, и не используют стандартные правила для обработки специальных значений, таких как NaN. Если код с плавающей запятой требует сохранения порядка и округления исходного кода или использует стандартное поведение специальных значений, используйте /fp:precise. Если код обращается к среде с плавающей запятой или изменяет режимы округления, отменяет исключения с плавающей запятой или проверяет состояние с плавающей запятой, используйте /fp:strict.

/fp:except

Параметр /fp:except создает код, чтобы гарантировать, что все исключения с плавающей запятой создаются в точной точке, в которой они происходят, и что другие исключения с плавающей запятой не создаются. По умолчанию параметр /fp:strict включает /fp:exceptи /fp:precise не поддерживает. Параметр /fp:except не совместим с /fp:fast. Параметр можно явно отключить с помощью /fp:except-.

Само по себе /fp:except не включает исключения с плавающей запятой. Однако для программ требуется включить исключения с плавающей запятой. Дополнительные сведения о включении исключений с плавающей запятой см. в разделе _controlfp.

Замечания

В одной командной строке компилятора можно указать несколько /fp параметров. Одновременно может применяться только один из /fp:strictвариантов /fp:fastи /fp:precise вариантов. Если в командной строке указано несколько этих параметров, более поздний параметр имеет приоритет, а компилятор создает предупреждение. Параметры /fp:strict и /fp:except параметры несовместимы с /clr.

Параметр /Za совместимости ANSI не совместим с /fp.

Использование директив компилятора для управления поведением с плавающей запятой

Компилятор предоставляет три директивы pragma для переопределения поведения с плавающей запятой, указанного в командной строке: float_control, fenv_accessи fp_contract. Эти директивы можно использовать для управления поведением с плавающей запятой на уровне функции, а не в функции. Эти директивы не соответствуют напрямую /fp параметрам. В этой таблице показано, как /fp параметры и директивы pragma сопоставляются друг с другом. Дополнительные сведения см. в документации по отдельным параметрам и директивам pragma.

Вариант 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

* В версиях Visual Studio до Visual Studio 2022 /fp:precise поведение по умолчанию.fp_contract(on)

Вариант 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

* В версиях Visual Studio начиная с Visual Studio 2022 /fp:precise поведение по умолчанию fp_contract(off).

Среда с плавающей запятой по умолчанию

При инициализации процесса устанавливается среда с плавающей запятой по умолчанию. Эта среда маскирует все исключения с плавающей запятой, задает режим округления до ближайшего (FE_TONEAREST), сохраняет ненормальные (денормальные) значения, использует точность знака по умолчанию (мантисса) для float, doubleа long double также значения, а также, где поддерживается, задает контроль бесконечности в режиме аффины по умолчанию.

Доступ и изменение среды с плавающей запятой

Среда выполнения Microsoft Visual C++ предоставляет несколько функций для доступа и изменения среды с плавающей запятой. К ним относятся _controlfp, _clearfpи _statusfp их варианты. Чтобы обеспечить правильное поведение программы при доступе к коду или изменении среды с плавающей запятой, fenv_access необходимо включить либо параметром, либо с помощью fenv_access pragma, чтобы эти функции могли иметь какой-либо /fp:strict эффект. Если fenv_access не включено, доступ или изменение среды с плавающей запятой может привести к непредвиденному поведению программы:

  • Код может не учитывать запрошенные изменения в среде с плавающей запятой.

  • Регистры состояния с плавающей запятой могут не сообщать о ожидаемых или текущих результатах.

  • Непредвиденные исключения с плавающей запятой могут возникать или ожидаемые исключения с плавающей запятой могут не возникать.

Когда код обращается к среде с плавающей запятой или изменяет его, необходимо быть осторожным при объединении кода, где fenv_access включен код с включенным кодом, который не fenv_access включен. В коде, где fenv_access не включена, компилятор предполагает, что среда с плавающей запятой по умолчанию по умолчанию действует. Кроме того, предполагается, что состояние с плавающей запятой недоступно или изменено. Рекомендуется сохранить и восстановить локальную среду с плавающей запятой в его состоянии по умолчанию перед передачей элемента управления в функцию, которая не включена fenv_access . В этом примере показано, как float_control можно задать и восстановить прагму:

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

Режимы округления с плавающей запятой

/fp:precise /fp:fastВ обоих условиях компилятор создает код, предназначенный для выполнения в среде с плавающей запятой по умолчанию. Предполагается, что среда не обращается к среде выполнения или не изменяется. То есть компилятор предполагает, что код никогда не распаковывает исключения с плавающей запятой, считывает или записывает регистры состояния с плавающей запятой или изменяет режимы округления. Однако некоторые программы должны изменить среду с плавающей запятой. Например, в этом примере вычисляются границы ошибок умножения с плавающей запятой путем изменения режимов округления с плавающей запятой:

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

Так как компилятор предполагает, что среда с плавающей запятой по умолчанию находится под /fp:fast и /fp:preciseне учитывает вызовы _controlfp_s. Например, при компиляции с помощью архитектуры /O2 /fp:precise x86 границы не вычисляются, а выходные данные примера программы:

cLower = -inf
cUpper = -inf

При компиляции с помощью архитектуры /O2 /fp:strict x86 пример программы выводит следующее:

cLower = -inf
cUpper = -3.40282e+38

Специальные значения с плавающей запятой

В /fp:precise и /fp:strict, выражения, которые включают специальные значения (NaN, +бесконечность, -бесконечность, -0,0) ведут себя в соответствии со спецификациями IEEE-754. В соответствии /fp:fastс поведением этих специальных значений может быть несовместимо с IEEE-754.

В этом примере показано другое поведение специальных значений в /fp:preciseразделе , /fp:strictи /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;
}

При компиляции с помощью /O2 /fp:precise архитектуры /O2 /fp:strict x86 выходные данные соответствуют спецификации IEEE-754:

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

При компиляции с помощью /O2 /fp:fast** для архитектуры x86 выходные данные не соответствуют IEEE-754:

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

Преобразования алгебраических с плавающей запятой

В /fp:precise разделе и /fp:strictкомпилятор не выполняет никакого математического преобразования, если преобразование не гарантируется для получения побитового идентичного результата. Компилятор может сделать такие преобразования в /fp:fast. Например, выражение a * b + a * c в примере функции algebraic_transformation может быть скомпилировано в a * (b + c) разделе /fp:fast. Такие преобразования не выполняются или /fp:strictне создаются /fp:precise компиляторомa * b + a * c.

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

Явные точки приведения с плавающей запятой

В /fp:precise разделе и /fp:strictкомпилятор округляется до точности исходного кода в четырех конкретных точках во время вычисления выражений: при назначении, типадресации, при передаче аргументов с плавающей запятой в вызов функции и при возврате вызова функции значение с плавающей запятой. Методы typecasts можно использовать для явного округления промежуточных вычислений. В разделе /fp:fastкомпилятор не создает явные приведения в этих точках, чтобы гарантировать точность исходного кода. В этом примере показано поведение в различных /fp вариантах:

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

При компиляции с помощью /O2 /fp:precise или /O2 /fp:strictможно увидеть, что явные приведения типов вставляются как в рассылку типов, так и в точке возврата функции в созданном коде архитектуры x64:

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

В /O2 /fp:fast созданном коде упрощено, так как все приведения типов оптимизированы:

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

Установка данного параметра компилятора в среде разработки Visual Studio

  1. Откройте диалоговое окно Страницы свойств проекта. Подробнее см. в статье Настройка компилятора C++ и свойства сборки в Visual Studio.

  2. Перейдите на страницу свойств Свойства конфигурации>C/C++>Создание кода.

  3. Измените свойство модели с плавающей запятой.

Установка данного параметра компилятора программным способом

См. также

Параметры компилятора MSVC
Синтаксис командной строки компилятора MSVC