2009-06-24 2 views
13

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

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

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

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

+0

Я не знаю C, но в .NET 1.1 до появления типов с нулевым значением многие люди использовали такие вещи, как минимальное значение (int.MinValue). Проблема с этим заключается в том, что вы должны учитывать его везде и гарантировать, что вы никогда не используете MinValue. Возможно, что-то такое, что существует для C? – RichardOD

+1

Я задал аналогичный (но не тот же) вопрос, возможно, ответы могут вам помочь. http://stackoverflow.com/questions/787828/nan-as-a-special-argument – quinmars

ответ

10

Только что заметил этот вопрос.

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

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

+0

Принято, потому что мы использовали NaN для неопределенного в этом случае, хотя оказалось, что это больше хлопот, чем ожидалось. Это было главным образом потому, что поддержка NaN в наших инструментах и ​​системах отсутствовала или искажалась, и нам приходилось работать над этим. – starblue

3

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

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

+1

Я думаю, что обоснование использования 0 состоит в том, что память инициализируется нулевыми байтами независимо от типа, например, в сегменте BSS C – starblue

+0

Да, наверное, это что-то вроде этого.Но теперь, когда разработчики языка/компилятора приложили усилия для инициализации памяти, не было бы почти легко инициализировать любое произвольное значение (отличное от нуля)? Нули - это всего лишь биты среди других :-) –

+2

@ mad-j: вы хотите инициализировать всю память с помощью одного и того же битового шаблона. Таким образом, этого не может быть 42, потому что тогда вам обычно приходится делать что-то другое для двух соседних шорт, чем то, что вы делаете для int. Это оставляет 0 и -1. Но 0xffffffff не является как float, поэтому у вас будет несогласованность. Там не так много, но я думаю, что 0, вероятно, лучше всего. Кроме того, некоторое аппаратное обеспечение может эффективно использовать всего целые блоки физической памяти сразу, для чего это стоит. –

0

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

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

+0

Не верьте этому ответу. Все, что должен сделать Джон Скит, это подумать о переменной, и она будет определять себя. –

+0

Значение определено до того, как вещи Skeet имеют имя переменной, не так ли? – glasnt

4

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

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

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

 
bool isOutsideRange(double x, double minValue, double maxValue) 
{ 
    return x < minValue || x > maxValue; 
} 

Если х NaN, то эта функция будет ошибочно сообщать, что х находится между MinValue и MaxValue.

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

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

+0

На каком языке используются эти правила сравнения? Возможно, D делает. Но по крайней мере C и C++ не работают с NaN таким образом. Все сравнительные сравнения будут ложными. x == NaN неверно для любого x, включая NaN. –

+1

Нет, ваша функция сообщает только, что она не находится за пределами диапазона. Это ни внутри, ни снаружи, что действительно может смутить тех, кто использует числа с плавающей точкой наивно. – starblue

+0

@ Igor: Мы говорим то же самое здесь. isOutsideRange вернет false, если x является NaN, что означает, что оно находится внутри диапазона, а это не так. – jskinner

1

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

2

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

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

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

+9

Err ... Разве это не точка NaNs, которую они будут распространять? Гораздо лучше иметь NaN в качестве результата, что указывает на то, что что-то не так, чем иметь невинно выглядящее, но абсолютно неправильное число (что может быть вызвано случайным использованием нулевых инициализированных чисел). –

+1

Да и нет, потому что, когда вы определяете NaN, только просматривая вывод или явно проверяя NaN. Следствием этого является то, что ошибки могут обнаруживаться гораздо позже, чем они возникают. С другой стороны, если вы используете NULL (если это возможно), вы быстро получаете ошибку NPE/сегментации. Жестокий, но эффективный. –

+0

Если все, что вы когда-либо знали, это то, что NaNs повсюду, это точно не поможет вам узнать, откуда они идут. – corsiKa

3

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

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

0

Это звучит для меня хорошо для меня. Хотел бы я подумать об этом ...

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

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

0

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

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