std :: isinf не работает с -ffast-math. как проверить на бесконечность

Образец кода:

#include <iostream>
#include <cmath>
#include <stdint.h>

using namespace std;

static bool my_isnan(double val) {
union { double f; uint64_t x; } u = { val };
return (u.x << 1) > 0x7ff0000000000000u;
}

int main() {
cout << std::isinf(std::log(0.0)) << endl;
cout << std::isnan(std::sqrt(-1.0)) << endl;
cout << my_isnan(std::sqrt(-1.0)) << endl;
cout << __isnan(std::sqrt(-1.0)) << endl;

return 0;
}

Онлайн компилятор.

С -ffast-math, этот код печатает «0, 0, 1, 1» — без, он печатает «1, 1, 1, 1».

Это верно? я думал так std::isinf/std::isnan должен еще работать с -ffast-math в этих случаях.

Кроме того, как я могу проверить бесконечность / NaN с -ffast-math? Вы можете увидеть my_isnan Делая это, и это на самом деле работает, но это решение, конечно, очень зависит от архитектуры. Кроме того, почему my_isnan работать здесь и std::isnan не? Как насчет __isnan а также __isinf, Они всегда работают?

С -ffast-mathчто является результатом std::sqrt(-1.0) а также std::log(0.0), Это становится неопределенным, или это должно быть NaN / -Inf?

Связанные обсуждения: (GCC) [Ошибка libstdc ++ / 50724] Новое: isnan прервано с помощью -ffinite-math-only в g ++, (Mozilla) Ошибка 416287 — возможность улучшения производительности с помощью isNaN

5

Решение

Обратите внимание, что -ffast-math может заставить компилятор игнорировать / нарушать спецификации IEEE, см. http://gcc.gnu.org/onlinedocs/gcc-4.8.2/gcc/Optimize-Options.html#Optimize-Options :

Эта опция не включена ни одной опцией -O, кроме -Ofast, так как
может привести к неправильному выводу для программ, которые зависят от точного
внедрение правил / спецификаций IEEE или ISO для математических функций.
Это может, однако, дать более быстрый код для программ, которые не требуют
гарантии этих спецификаций.

Таким образом, используя -ffast-math вам не гарантировано видеть бесконечность там, где вы должны.

Особенно, -ffast-math включается -ffinite-math-only, увидеть http://gcc.gnu.org/wiki/FloatingPointMath что означает (от http://gcc.gnu.org/onlinedocs/gcc-4.8.2/gcc/Optimize-Options.html#Optimize-Options )

[…] оптимизация для арифметики с плавающей точкой, которая предполагает, что аргументы и результаты не являются NaN или + -Infs

Это означает, что, позволяя -ffast-math вы даете обещание компилятору, что ваш код никогда не будет использовать бесконечность или NaN, что в свою очередь позволяет компилятору оптимизировать код, например, заменяя любые вызовы isinf или же isnan по константе false (и дальше оптимизировать оттуда). Если вы нарушаете свое обещание компилятору, компилятор не обязан создавать правильные программы.

Таким образом, ответ довольно прост, если ваш код может иметь бесконечности или NaN (что настоятельно подразумевается тем, что вы используете isinf а также isnan), вы не можете включить -ffast-math иначе вы можете получить неправильный код.

Ваша реализация my_isnan работает (в некоторых системах), потому что он напрямую проверяет двоичное представление числа с плавающей запятой. Конечно, процессор все еще может выполнять (некоторые) фактические вычисления (в зависимости от того, какие оптимизации выполняет компилятор), и, таким образом, фактические NaN могут появляться в памяти, и вы можете проверить их двоичное представление, но, как объяснено выше, std::isnan возможно, был заменен константой false, Также может случиться так, что компилятор заменит, например, sqrtпо какой-то версии, которая даже не производит NaN для ввода -1, Чтобы увидеть, какие оптимизации выполняет ваш компилятор, скомпилируйте его на ассемблер и посмотрите на этот код.

Чтобы сделать (не полностью несвязанную) аналогию, если вы говорите своему компилятору, что ваш код находится на C ++, вы не можете ожидать, что он правильно скомпилирует код C и наоборот (для этого есть реальные примеры, например, Может ли код, действительный как на C, так и на C ++, вызывать различное поведение при компиляции на каждом языке? ).

Это плохая идея, чтобы включить -ffast-math и использовать my_isnan поскольку это сделает все очень зависимым от машины и компилятора, вы не знаете, какие оптимизации выполняет компилятор в целом, поэтому могут быть и другие скрытые проблемы, связанные с тем, что вы используете неконечные математические вычисления, но скажите компилятору иначе.

Простое исправление заключается в использовании -ffast-math -fno-finite-math-only что все равно даст некоторые оптимизации.

Также возможно, что ваш код выглядит примерно так:

  1. отфильтровать все бесконечности и NaNs
  2. сделать некоторые конечные математические вычисления для отфильтрованных значений (под этим я подразумеваю математические операции, которые гарантированно никогда не создадут бесконечности или NaN, это должно быть очень, очень тщательно проверено)

В этом случае вы можете разделить ваш код и использовать оптимизировать #pragma или же __attribute__ превратить -ffast-math (соответственно -ffinite-math-only а также -fno-finite-math-only) включать и выключать выборочно для заданных фрагментов кода (однако, я помню, что были некоторые проблемы с какой-то версией GCC, связанной с этим), или просто разбить ваш код на отдельные файлы и скомпилировать их с разными флагами. Конечно, это также работает в более общих настройках, если вы можете изолировать части, где могут возникнуть бесконечности и NaN. Если вы не можете изолировать эти части, это явный признак того, что вы не можете использовать -ffinite-math-only для этого кода.

Наконец, важно понимать, что -ffast-math это не безопасная оптимизация, которая просто делает вашу программу быстрее. Это влияет не только на производительность вашего кода, но и на его корректность (и это, помимо всего прочего, уже над всеми проблемами, касающимися чисел с плавающей запятой, если я правильно помню Уильям Кахан имеет коллекцию страшных историй на своей домашней странице, см. также Что должен знать каждый программист об арифметике с плавающей точкой). Короче говоря, вы можете получить более быстрый код, но также ошибочные или неожиданные результаты (пример приведен ниже). Следовательно, вы должны использовать такие оптимизации только тогда, когда вы действительно знаете, что делаете, и вы абсолютно уверены, что либо

  1. оптимизации не влияют на правильность этого конкретного кода, или
  2. ошибки, вносимые оптимизацией, не являются критическими для кода.

Программный код может вести себя совершенно по-разному, в зависимости от того, используется эта оптимизация или нет. В частности, он может вести себя неправильно (или, по крайней мере, очень противоречит вашим ожиданиям), когда такие оптимизации, как -ffast-math включены Возьмите следующую программу, например:

#include <iostream>
#include <limits>

int main() {
double d = 1.0;
double max = std::numeric_limits<double>::max();
d /= max;
d *= max;
std::cout << d << std::endl;
return 0;
}

будет производить продукцию 1 как и ожидалось при компиляции без какого-либо флага оптимизации, но с использованием -ffast-mathбудет выводить 0,

12

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]