У нас есть числовой код, написанный на C ++. Редко, но при определенных конкретных входных данных, некоторые вычисления приводят к значению ‘nan’.
Существует ли стандартный или рекомендуемый метод, с помощью которого мы можем остановить и предупредить пользователя, когда определенный численный расчет приводит к генерированию «нан»? (в режиме отладки). Проверка для каждого результата, если он равен ‘nan’, кажется непрактичной, учитывая огромные размеры матриц и векторов.
Как стандартные числовые библиотеки справляются с этой ситуацией? Не могли бы вы пролить свет на это?
NaN распространяется, когда применяется к числовой операции. Таким образом, достаточно проверить окончательный результат для NaN. Что касается того, как это сделать — если сборка для> = C ++ 11, есть std :: isnan, как заметил Гоз. За < C ++ 11 — если хотите быть пуленепробиваемым — я бы лично проверил битовую проверку (особенно, если это может быть связано с оптимизацией). Шаблон для NaN является
? 11.......1 xx.......x
sign bit ^ ^exponent^ ^fraction^
где ? может быть что угодно, и хотя бы один Икс должно быть 1.
Для решения, зависящего от платформы, есть еще одна возможность. Есть функция feenableexcept
в Glibc (вероятно, с signal
функция и опция компилятора -fnon-call-exceptions
), который включает поколение SIGFPE
sinals, когда происходит недопустимая операция с плавающей точкой. И функция _control87
(вероятно, с _set_se_translator
функция и опция компилятора /EHa
), что позволяет в значительной степени то же самое в VC.
Хотя это нестандартное расширение от glibc, на многих системах вы можете использовать feenableexcept
рутина объявлена в <fenv.h>
запросить, чтобы машина перехватила определенные исключения с плавающей точкой и доставила SIGFPE
к вашему процессу. Ты можешь использовать fedisableexcept
маскировать ловушку, и fegetexcept
запросить набор исключений, которые не маскируются. По умолчанию все они замаскированы.
На старых системах BSD без этих подпрограмм вы можете использовать fpsetmask
а также fpgetmask
от <ieeefp.h>
вместо этого, но мир, кажется, сходится на API glibc.
Предупреждение: в настоящее время в glibc есть ошибка, из-за которой (стандартная процедура C99) fegetenv
имеет непреднамеренный побочный эффект маскировки всех ловушек исключений на x86, поэтому вам нужно вызвать fesetenv
чтобы восстановить их потом. (Показывает, как сильно кто-то полагается на этот материал …)
На многих архитектурах вы можете разоблачать недопустимое исключение, которое вызовет прерывание, когда NaN обычно генерируется вычислением, таким как 0*infinity
, Запустив в отладчике, вы прерветесь на этом прерывании и сможете проверить вычисления, которые привели к этой точке. Вне отладчика вы можете установить обработчик прерываний для регистрации информации о состоянии вычислений, которые произвели недопустимую операцию.
Например, в x86 вы должны очистить бит маски недопустимой операции в FPCR (бит 0) и MXCSR (бит 7), чтобы включить перехват недопустимых операций из операций x87 и SSE соответственно.
Некоторые отдельные платформы предоставляют средства для записи в эти управляющие регистры из C, но нет переносимого интерфейса, который работает кроссплатформенно.
Тестирование f! = F может привести к проблемам при использовании g ++ с включенной оптимизацией -ffast-math: Проверка, является ли double (или float) NaN в C ++
Единственный надежный способ — проверить битовый паттерн.
Относительно того, где выполнять проверки, это действительно зависит от специфики ваших вычислений и от того, насколько часты ошибки Nan, то есть снижение производительности из-за продолжающихся неконтролируемых вычислений по сравнению с проверками на определенных этапах.