Подтвердить что ты не робот

Бесконечность не 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;
4b9b3361

Ответ 1

Я не особенно знаком с правилами с плавающей запятой, но я подозреваю, что мы могли бы бежать от [expr]/4

Если во время оценки выражения результат не определяется математически или нет в диапазоне представляемых значений для его типа, поведение undefined.

Что, в свою очередь, означает, что мы запускаем [expr.const]/2.6:

Выражение e является выражением основной константы, если оценка e, следуя правилам абстрактной машины, не будет оценивать одно из следующих выражений: [...] операция, которая имела бы поведение undefined, как указано в [intro] через [cpp] этого документа

Это означает, что инициализатор для foo не является константным выражением, поэтому мы не можем инициализировать объект constexpr с ним.


Если infinity() - epsilon() четко определено для float, это ошибка clang, код хорошо сформирован. Если он не определен для float, это ошибка gcc.