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

Использование NaN в С++?

Какой лучший способ использовать NaN в С++?

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

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

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

Можно ли использовать signaling_NaN без привлечения исключения при присваивании? Есть ли хорошая, переносимая альтернатива signaling_NaN, которая будет поднимать исключение с плавающей запятой при использовании?

4b9b3361

Ответ 1

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

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

Итак, кажется, что решение Motti - лучший выбор.

Ответ 2

То, что сигнализирует 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; 
   }
};

Ответ 3

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

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

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

Ответ 4

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

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

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

double value = _Nan._Double;

Ответ 5

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

#define NegativeNaN log(-1)

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

Ответ 6

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