Это хорошая идея использовать IEEE754 с плавающей точкой NaN для значений, которые не установлены?

Является ли хорошей идеей использовать NaN IEEE754 с плавающей точкой (не число) для значений, которые не определены по нематематическим причинам?

В нашем случае они еще не установлены, поскольку значения не были получены от какого-либо другого устройства. Контекст является встроенной системой, использующей значения IEC1131 REAL32. Редактировать: язык программирования - C, поэтому мы, скорее всего, будем использовать NAN и isnanf(x) из C99. Хотя нам, возможно, понадобятся некоторые дополнительные искажения, чтобы включить их в наш уровень совместимости с ОС.

По умолчанию в языках программирования инициализируются переменные с плавающей запятой положительным нулем, внутренним представлением которого являются все нули. Это не для нас, потому что 0 находится в диапазоне допустимых значений.

Это похоже на чистое решение использовать NaN, но, может быть, это более хлопотно, чем стоит, и мы должны выбрать другое значение?

9 ответов

Решение

Просто заметил этот вопрос.

Это одно из применений NaN, которое имеет в виду комитет IEEE 754 (я был членом комитета). Правила распространения для NaN в арифметике делают это очень привлекательным, потому что, если у вас есть результат из длинной последовательности вычислений, которая включает в себя некоторые инициализированные данные, вы не ошибетесь в результате получения действительного результата. Это также может упростить отслеживание ваших расчетов, чтобы найти, где вы используете инициализированные данные.

Тем не менее, есть несколько подводных камней, которые находятся вне контроля комитета 754: как отметили другие, не все аппаратные средства поддерживают значения NaN на скорости, что может привести к снижению производительности. К счастью, редко приходится выполнять много операций с инициализированными данными в условиях, критичных к производительности.

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

  • if (result == DEFAULT_VALUE), не будет работать, как ожидалось, если DEFAULT_VALUE это NaN, как упоминал Джон.

  • Они также могут вызвать проблемы с проверкой дальности, если вы не будете осторожны. Рассмотрим функцию:

bool isOutsideRange (double x, double minValue, double maxValue)
{
    вернуть x  maxValue;
}

Если x равен NaN, эта функция неправильно сообщит, что x находится между minValue и maxValue.

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

[Редактировать: мне сначала удалось напечатать "любые сравнения, связанные с ними, будут правдой" выше, что не является тем, что я имел в виду, и это неправильно, все они ложны, кроме NaN!= NaN, что верно]

Я думаю, что это плохая идея в целом. Следует иметь в виду, что большинство процессоров обрабатывают Nan гораздо медленнее, чем "обычное" плавание. И трудно гарантировать, что у вас никогда не будет Nan в обычных условиях. Мой опыт работы с цифровыми вычислениями заключается в том, что они часто приносят больше проблем, чем они того стоят.

Правильное решение состоит в том, чтобы избежать кодирования "отсутствия значения" в float, но сигнализировать об этом по-другому. Это не всегда практично, в зависимости от вашей кодовой базы.

Будьте осторожны с NaN... они могут распространяться как лесной пожар, если вы не будете осторожны.

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

D использует это в качестве обоснования для предоставления чисел NaN по умолчанию. (С чем я не уверен, я согласен.)

Я использовал NaN в аналогичных ситуациях только из-за этого: обычное значение инициализации по умолчанию 0 также является допустимым значением. До сих пор NaNs работают нормально.

Кстати, это хороший вопрос, почему значение инициализации по умолчанию обычно (например, в примитивных типах Java) 0, а не NaN. Разве это не может быть 42 или что-то еще? Интересно, каково обоснование нулей?

Я чувствую, что это немного странно, но, по крайней мере, любые другие числа, которые вы выполняете с этим значением NaN, дают NaN как результат - когда вы видите NaN в отчете об ошибке, по крайней мере, вы знаете, какую ошибку вы преследуете.

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

Просто помните, что в зависимости от вашей среды вам, вероятно, понадобится особый способ обнаружения NaN (не просто используйте if (x == float.NaN) или каков твой эквивалент.)

Это звучит как хорошее применение для нанс для меня. Жаль, что я не думал об этом...

Конечно, они должны распространяться как вирус, вот в чем дело.

Я думаю, что я бы использовал Nan вместо одной из бесконечностей. Возможно, было бы неплохо использовать сигнальный нан и заставить его вызывать событие при первом использовании, но к тому времени уже слишком поздно, чтобы при первом использовании он успокоился.

Использование NaN в качестве значения по умолчанию является разумным.

Обратите внимание, что некоторые выражения, такие как (0.0 / 0.0), возвращают NaN.

Другие вопросы по тегам