Различная оптимизация в VS2015 и VS2013 вызывает исключение с плавающей запятой

У меня есть небольшой пример проблемы, возникшей при переходе с VS2013 на VS2015. В VS2015 приведенный далее пример кода вызывает недопустимую операцию с плавающей запятой.

введите здесь описание изображения

int main()
{
    unsigned int enableBits = _EM_OVERFLOW | _EM_ZERODIVIDE | _EM_INVALID;

    _clearfp();
    _controlfp_s(0, ~enableBits, enableBits);

    int count = 100;
    float array[100];

    for (int i = 0; i < count; ++i)
    {
        array[i] = (float)pow((float)(count - 1 - i) / count, 4); //this causes exception in VS2015
    }

    return 0;
}

Это происходит только в режиме выпуска, поэтому, вероятно, это вызвано другой оптимизацией. Что-то не так с этим кодом или это ошибка в VS 2015?

Трудно найти подобные проблемы во всей базе кода, поэтому я ищу какое-то систематическое исправление, а не обходной путь (например, используйте другую переменную вместо i, которая работает)

Я также проверил сгенерированный код сборки, и кажется, что в VS2013 он использует весь 128-битный реестр для выполнения 4 операций с плавающей запятой в одном делении. В VS2015, похоже, выполняются только 2 операции с плавающей запятой, а остальная часть реестра равна нулю (или какой-то мусор), что, вероятно, приводит к этому исключению.

Инструкция, вызывающая исключение, отмечена на картинке.

VS2013 VS2013

и VS2015 введите здесь описание изображения

Любая помощь будет оценена. Спасибо.


comment
Хм. Ваш код работает для меня (Release/x64) с включенной полной оптимизацией. Ваш VS обновлен? У вас есть какие-то специальные настройки компилятора?   -  person Simon Kraemer    schedule 11.10.2016
comment
Я использовал обновление 3 VS2015. Я использовал стандартные настройки для выпуска + Оптимизация (Максимальная скорость / O2) и активировал встроенные функции (Да / Oi)   -  person Bezousek    schedule 11.10.2016


Ответы (1)


Это похоже на взаимодействие с вами с использованием исключений с плавающей запятой, но также с некоторыми оптимизациями с плавающей запятой.

Что делает код, так это то, что он выполняет 2 итерации одновременно (развертывание цикла), но использует divps, который выполняет 4 деления одновременно (из 4 чисел с плавающей запятой в регистре XMM). Верхние 2 числа с плавающей запятой в регистре XMM не используются и равны нулю. Поскольку в результате деления значения в этих слотах не используются, это обычно не имеет значения. Однако, когда вы устанавливаете пользовательскую обработку исключений, это вызывает недопустимое исключение операции, которое вы видите, даже если его генерируемые значения не будут использоваться.

Ваш выбор, как я вижу, состоит в том, чтобы установить /fp:strict, который отключит оптимизацию, поэтому заставьте это работать (но это, очевидно, сделает код медленнее) или удалите вызов controlfp.

person Mike Vine    schedule 11.10.2016
comment
Спасибо за комментарий. Эти строгие настройки помогут, но проблема в том, что я не знаю, где я должен это установить. У меня обычно сотни файлов с вычислениями, и я не хочу уменьшать их везде. Знаете ли вы, что происходит в VS2015 и VS2013, где он работает нормально даже на /fp:precise? - person Bezousek; 11.10.2016