Вопрос

Как лучше всего использовать NaN в C ++?

Я нашел std::numeric_limits<double>::quiet_NaN() и std::numeric_limits<double>::signaling_NaN(). Я хотел бы использовать signaling_NaN для представления неинициализированной переменной следующим образом:

double diameter = std::numeric_limits<double>::signaling_NaN();

Это, однако, сигнализирует (вызывает исключение) при назначении. Я хочу, чтобы это вызывало исключение при использовании, а не при назначении.

Есть ли способ использовать <=>, не вызывая исключения при назначении? Есть ли хорошая, переносимая альтернатива <=>, которая вызовет исключение с плавающей запятой при использовании?

Это было полезно?

Решение

Посмотрев на это еще раз, похоже, что signaling_NaN бесполезен, как это предусмотрено. Если исключения с плавающей запятой включены, то вызов его считается обработкой сигнального NaN, поэтому сразу возникает исключение. Если исключения с плавающей запятой отключены, то обработка сигнального NaN автоматически понижает его до тихого NaN, поэтому <=> в любом случае не работает.

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

Похоже, Решение Motti действительно лучший выбор.

Другие советы

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

template <class T>
class initialized {
    T t;
    bool is_initialized;
public:
    initialized() : t(T()), is_initialized(false) { }
    initialized(const T& tt) : t(tt), is_initialized(true) { }
    T& operator=(const T& tt) { t = tt; is_initialized = true; return t; }
    operator T&() {
         if (!is_initialized)
             throw std::exception("uninitialized");
         return t; 
   }
};

Вы можете записать сигнальный NaN в переменную, не вызывая исключения с чем-то вроде этого (nb: untested)

void set_snan( double &d )
{
    long long *bits = (long long *)&d;
    *bits = 0x7ff0000080000001LL;
}

Он будет работать в большинстве мест, но нет, он не на 100% портативен.

Ну, глядя на определение как тихого, так и сигнального NaN, я не могу разглядеть никакой разницы.

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

Если вы хотите напрямую назначить NaN:

double value = _Nan._Double;

Простой ответ: Сделайте что-то подобное в заголовочном файле и используйте его везде:

#define NegativeNaN log(-1)

Если вы хотите выполнить какие-то манипуляции с ними, лучше напишите некоторую расширенную функцию-обертку вокруг exp(), например extended_exp() и так далее!

Ваша реализация C ++ может иметь API для доступа к среде с плавающей запятой для проверки и очистки определенных исключений с плавающей запятой. См. мой ответ на связанный вопрос для получения дополнительной информации.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top