КРАТКОЕ ОПИСАНИЕ: для строгой переносимости не используйте NaN. Используйте отдельный действительный бит. Например. шаблон как действительный. Однако, если вы знаете , что вы когда-либо будете работать только на машинах IEEE 754-2008, а не на IEEE 754-1985 (см. Ниже), то вам это может сойти с рук.
Для повышения производительности, вероятно, быстрее не использовать NaN на большинстве машин, к которым у вас есть доступ. Тем не менее, я участвовал в разработке аппаратного обеспечения FP на нескольких машинах, которые улучшают производительность обработки NaN, поэтому существует тенденция ускорять работу NaN, и, в частности, сигнализация NaN должна скоро быть быстрее, чем Valid.
ДЕТАЛЬ:
Не все форматы с плавающей запятой имеют NaN. Не все системы используют IEEE с плавающей запятой. Шестнадцатеричное число с плавающей точкой IBM все еще можно найти на некоторых машинах - фактически в системах, поскольку IBM теперь поддерживает IEEE FP на более поздних машинах.
Кроме того, в самой IEEE Floating Point были проблемы совместимости с NaN, в IEEE 754-1985. Например, см. Википедию 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 /apers / x86andinfinity.html говорит: «Intel Pentium 4 очень плохо обрабатывает бесконечности, NAN и денормалы ... ... Если вы пишете код, который добавляет числа с плавающей запятой со скоростью по одному на такт, а затем добавляем бесконечность в качестве входных данных, производительность падает. Много. Огромное количество ... 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 «Если вы выполняете математику для массивов типа double, которые содержат большое количество NaN или Infinities, существует штраф на порядок величины производительности».
Если я чувствую себя побужденным, я могу пойти и запустить микробенчмарк на некоторых машинах. Но вы должны получить картину.
Это должно измениться, потому что не так сложно быстро сделать QNaN.Но это всегда была проблема курицы и яйца: парни из аппаратных средств, подобные тем, с которыми я работаю, говорят: «Никто не использует NaN, поэтому мы не выигрываем; они делают это быстро», в то время как парни из программного обеспечения не используют NaN, потому что они медленные.Тем не менее, прилив медленно меняется.
Черт, если вы используете gcc и хотите добиться максимальной производительности, вы включаете оптимизацию, например "-ffinite-math-only ..." Разрешить оптимизацию для арифметики с плавающей точкой, которая предполагаетчто аргументы и результаты не являются NaNs или + -Infs. "Подобное верно для большинства компиляторов.
Кстати, вы можете, как и я, гуглить "NaN производительность с плавающей запятой" и проверить ссылки самостоятельно.И / или запустите свои собственные микробенчмарки.
Наконец, я предполагал, что вы используете шаблон, подобный
template<typename T> class Valid {
...
bool valid;
T value;
...
};
Мне нравятся шаблоны, подобные этому, потому что они могут принести «отслеживание действительности»не только для FP, но также для целых (действительных) и т. д.
Но они могут иметь большую стоимость.Операции, вероятно, не намного дороже, чем обработка 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; };
, если вы не используете NaN - или какое-либо другое специальное кодирование.
И
struct Point_with_Valid_Coords { float x, y, z; bool valid_x, valid_y, valid_z };
находится между ними - но тогда вам придется делать весь код самостоятельно.
Кстати, я предполагаю, что вы используете C ++.Если FORTRAN или Java ...
BOTTOM LINE: отдельные действительные биты, вероятно, быстрее и более переносимы.
Но обработка NaN ускоряется, и скоро один день будет достаточно хорош
Кстати, мои предпочтения: создать шаблон Valid.Тогда вы можете использовать его для всех типов данных.Специализируйте это для NaNs, если это помогает.Хотя моя жизнь делает вещи быстрее, ИМХО, как правило, важнее сделать код чистым.