Ответ 1
КРАТКОЕ ОПИСАНИЕ: Для обеспечения максимальной мобильности не используйте NaN. Используйте отдельный допустимый бит. Например. шаблон, например Valid. Однако, если вы знаете, что вы будете работать только на машинах IEEE 754-2008, а не на IEEE 754-1985 (см. Ниже), то вы можете с ним справиться.
Для производительности, скорее всего, быстрее не использовать NaN на большинстве машин, к которым у вас есть доступ. Тем не менее, я занимался аппаратным дизайном FP на нескольких машинах, которые улучшают производительность обработки NaN, поэтому существует тенденция к тому, что NaNs быстрее, и, в частности, сигнализация NaN должна скоро быть быстрее, чем Действительная.
ДЕТАЛЬ:
Не все форматы с плавающей запятой имеют NaN. Не во всех системах используется плавающая точка IEEE. Ячейка с шестью точками с шестью точками IBM все еще может быть найдена на некоторых машинах - на самом деле это системы, поскольку IBM теперь поддерживает IEEE FP на более поздних машинах.
Кроме того, у IEEE Floating Point были проблемы с совместимостью с NaNs в IEEE 754-1985. Например, см. Wikipedia http://en.wikipedia.org/wiki/NaN:
Оригинальный стандарт IEEE 754 от 1985 (только IEEE 754-1985) описал двоичные форматы с плавающей запятой и не указал, как сигнализированное/тихое состояние должно быть помечено. На практике большинство Значительный бит значимости определял, является ли NaN сигнализация или тишина. Две различные реализации с обратным значения. * большинство процессоров (в том числе семейства Intel/AMD x86-32/x86-64, семейства Motorola 68000, семейства AIM PowerPC, ARM семейство и семейство Sun SPARC) устанавливают бит сигнализации/ ненулевое, если NaN тихо, и к нулю, если NaN сигнализирует. Таким образом, на этих процессорах бит представляет собой флаг "is_quiet". * в NaN, генерируемых процессорами PA-RISC и MIPS, бит с сигналом/типом равен нулю, если NaN является тихим и отличным от нуля, если NaN сигнализирует. Таким образом, на этих процессорах бит представляет собой флаг is_signaling.
Это, если ваш код может работать на старых машинах HP или на существующих машинах MIPS (которые повсеместно распространены в встроенных системах), вы не должны зависеть от фиксированной кодировки NaN, но должны иметь зависимые от машины #ifdef для вашего специального NaNs.
IEEE 754-2008 стандартизирует кодировки NaN, так что это улучшается. Это зависит от вашего рынка.
Что касается производительности: многие машины, по сути, ловушки или, как правило, делают большую икону в производительности, при выполнении вычислений, включающих как SNaN (которые должны ловушки), так и QNaN (которые не нужно ловушки, то есть, которые могут быть быстрыми, и которые становятся быстрее на некоторых машинах, когда мы говорим.)
Я могу с уверенностью сказать, что на старых машинах, особенно на старых компьютерах Intel, вы НЕ хотели использовать NaN, если бы вы заботились о производительности. Например. http://www.cygnus-software.com/papers/x86andinfinity.html говорит: "Intel Pentium 4 очень плохо справляется с бесконечностями, NAN и denormals.... Если вы пишете код, который добавляет числа с плавающей запятой со скоростью одного за каждый такт, а затем бросать в него бесконечности в качестве входных данных, производительность падает... Много... Огромное количество... NAN еще медленнее. Дополнение с NAN занимает около 930 циклов... Денормалы немного сложнее измерить."
Получить изображение? Почти 1000 раз медленнее использовать NaN, чем выполнять обычную операцию с плавающей запятой? В этом случае почти гарантировано, что использование шаблона типа Valid будет быстрее.
Однако см. ссылку на "Pentium 4"? Это действительно старая веб-страница. В течение многих лет такие люди, как я, говорили, что "QNaNs должны быть быстрее", и он медленно овладевает.
Совсем недавно (2009) Microsoft говорит http://connect.microsoft.com/VisualStudio/feedback/details/498934/big-performance-penalty-for-checking-for-nans-or-infinity "Если вы делаете математику на массивах двойных, содержащих большое количество NaN или Infinities, есть заказ от величины штрафа за производительность".
Если я чувствую себя побужденным, я могу пойти и запустить микрообъект на некоторых машинах. Но вы должны получить картину.
Это должно меняться, потому что не так быстро делать QNaNs. Но это всегда была проблема с курицей и яйцом: аппаратные парни вроде тех, с кем я работаю, говорят: "Никто не использует NaNs, поэтому мы победили, сделаем их быстрыми", в то время как программные ребята не используют NaN, потому что они медленны. Тем не менее, поток медленно меняется.
Если вы используете gcc и хотите получить лучшую производительность, вы включаете такие оптимизации, как "-ffinite-math-only... Разрешить оптимизацию для арифметики с плавающей запятой, которые предполагают, что аргументы и результаты не являются NaN или + -Infs". Аналогично для большинства компиляторов.
Кстати, вы можете google, как я, "с плавающей запятой производительности NaN" и проверить, что вы выбрали. И/или запускайте свои собственные микрообъекты.
Наконец, я предполагаю, что вы используете шаблон, например
template<typename T> class Valid {
...
bool valid;
T value;
...
};
Мне нравятся шаблоны, подобные этому, потому что они могут приводить "отслеживание достоверности" не только к FP, но и к целому (Valid) и т.д.
Но у них может быть большая стоимость. Операции, вероятно, не намного дороже обработки NaN на старых машинах, но плотность данных может быть очень низкой. sizeof (Действительный) может иногда быть 2 * sizeof (float). Эта плохая плотность может повредить производительность намного больше, чем операции.
Кстати, вам следует рассмотреть специализированную специализацию, так что Valid использует NaN, если они доступны и быстры, и в противном случае допустимый бит.
template <> class Valid<float> {
float value;
bool is_valid() {
return value != my_special_NaN;
}
}
и др.
В любом случае, вам лучше иметь как можно меньше действительных битов и упаковывать их в другое место, а не Действовать справа от значения. Например.
struct Point { float x, y, z; };
Valid<Point> pt;
лучше (плотность), чем
struct Point_with_Valid_Coords { Valid<float> x, y, z; };
если вы не используете NaNs или какую-либо другую специальную кодировку.
И
struct Point_with_Valid_Coords { float x, y, z; bool valid_x, valid_y, valid_z };
находится между ними, но тогда вы должны сами выполнять весь код.
Кстати, я предполагаю, что вы используете С++. Если FORTRAN или Java...
BOTTOM LINE: отдельные допустимые биты, вероятно, быстрее и более переносимы.
Но управление NaN ускоряется, и в один прекрасный день скоро будет достаточно хорошо
Кстати, мое предпочтение: создать шаблон Valid. Затем вы можете использовать его для всех типов данных. Специализируйте его для NaNs, если это поможет. Хотя моя жизнь ускоряет работу, ИМХО обычно важнее сделать код чистым.