Я только что обнаружил, что при делении числа с плавающей запятой на бесконечность при компиляции с любым параметром оптимизации получается другое значение NaN для clang.
std::numeric_limits<float>::infinity()/std::numeric_limits<float>::infinity()
приводит к следующим наборам битов:
01111111110000000000000000000000 при компиляции с -O1 и11111111110000000000000000000000 при компиляции без него.Разница только в знаковом бите.
Я предполагаю, что решение о том, как представлять NaN, зависит от компилятора, но я удивлен, что мы увидим разные представления в одном и том же компиляторе. GCC и MSVC кажутся согласованными (11111111110000000000000000000000) независимо от оптимизации.
Мои вопросы:
Пожимаю плечами. Получение NaN указывает на то, что в расчетах что-то пошло не так. Поэтому следующий шаг — отбросить результат или, возможно, переделать расчет по-другому. Попытка разобрать NaN бывает полезной лишь в редких случаях. Обратите внимание, что IEEE-754 не предусматривает различных битовых комбинаций для NaN; это разработка Intel, которая не обязательно поддерживается на другом оборудовании.
Насколько я помню, IEEE-754 указывает только, что результатом операции над NAN является NAN, он ничего не говорит о конкретном битовом шаблоне, и я не понимаю, чем это может быть полезно.
@Neal Kruis Что произойдет, если вы добавите -ffp-model=precise или -ffp-model=strict после указания -O<level>? Хотя мне также нравится идиома INFINITY/INFINITY для создания NaN, вы можете попробовать использовать предопределенный макрос NAN из cmath.
-fno-trapping-math позволяет gcc вычислить деление во время компиляции и выдать +NaN.
Спасибо, @njuffa и @Marc Glisse! -ffp-model=precise и -fno-trapping-math оба дают стабильные результаты. Тем не менее, по-прежнему ясно, что C++ ничего не гарантирует относительно знакового бита в NaN, и на него нельзя полагаться при различении различных типов NaN.





Должны ли мы ожидать согласованного результата для всех компиляторов независимо от оптимизации?
Должны ли мы ожидать стабильного результата в рамках одного компилятора независимо от оптимизации?
Поведение, о котором вы сообщаете для Clang и других компиляторов, соответствует как C++, так и IEEE-754, даже если знаковый бит меняется от обстоятельства к обстоятельству, как показано ниже.
Сканирование стандарта C++ (проект N4849 2020 г.) не показывает ничего, что определяло бы, каким должен быть знак результата NaN.
C++ не требует, чтобы реализации соответствовали IEEE-754. Тем не менее, если предположить, что реализация это делает, то IEEE-754 2019 (проект D2.47, март 2019 г.) 6.3 «Знаковый бит» говорит:
… Для всех остальных операций в настоящем стандарте знак не указывается бит результата NaN, даже если имеется только один входной NaN или когда NaN получен из недопустимого значения. операция…
«все остальные операции» включают в себя деление, поскольку до этого обсуждались операции copy, negate, abs, copySign, totalOrder и isSignMinus.
Не уверен, что
-O1включает-ffast-mathна clang, но для проверки добавьте-fno-fast-math, чтобы убедиться, что он выключен, а затем проверьте.