Бесконечность не constexpr
Я хотел проверить поведение поплавков около бесконечности.
Для этого я наивно написал следующий код:
#include <limits>
#include <iostream>
int main() {
constexpr float foo = std::numeric_limits<float>::infinity() - std::numeric_limits<float>::epsilon();
std::cout << foo << std::endl;
return foo;
}
Интересная часть для меня заключалась в том, что она компилируется в GCC 7.2, но терпит неудачу на Clang 5 (жалуется на назначение неконстантсприваром foo
).
AFAIK, так как С++ 11, std::numeric_limits<float>::infinity()
и infinity()
являются constexpr
, поэтому мне интересно, где проблема лежит для Clang.
РЕДАКТИРОВАТЬ 1:
Удалено ненужное static_assert
.
Спасибо, что указали на деление на 0. ИМО здесь цитируемый текст стандартов здесь не применим!?
И обязательная ссылка godbolt: https://godbolt.org/g/Nd5yF9
ИЗМЕНИТЬ 2:
Обратите внимание, что такое же поведение относится к:
constexpr float foo = std::numeric_limits<float>::infinity() - 100.0f;
Ответы
Ответ 1
Я не особенно знаком с правилами с плавающей запятой, но я подозреваю, что мы могли бы бежать от [expr]/4
Если во время оценки выражения результат не определяется математически или нет в диапазоне представляемых значений для его типа, поведение undefined.
Что, в свою очередь, означает, что мы запускаем [expr.const]/2.6:
Выражение e является выражением основной константы, если оценка e, следуя правилам абстрактной машины, не будет оценивать одно из следующих выражений: [...] операция, которая имела бы поведение undefined, как указано в [intro] через [cpp] этого документа
Это означает, что инициализатор для foo
не является константным выражением, поэтому мы не можем инициализировать объект constexpr
с ним.
Если infinity() - epsilon()
четко определено для float
, это ошибка clang, код хорошо сформирован. Если он не определен для float
, это ошибка gcc.