Сигнализация или перехват «нан», когда они происходят в вычислениях в базе числового кода в С++

У нас есть числовой код, написанный на C++. В редких случаях, но при определенных входных данных, некоторые вычисления приводят к значению «нан».

Существует ли стандартный или рекомендуемый метод, с помощью которого мы можем остановить и предупредить пользователя, когда определенный числовой расчет приводит к созданию «нан»? (в режиме отладки). Проверка каждого результата на равенство «нан» кажется непрактичной, учитывая огромные размеры матриц и векторов.

Как стандартные числовые библиотеки справляются с этой ситуацией? Не могли бы вы пролить свет на это?


person hAcKnRoCk    schedule 16.05.2013    source источник


Ответы (4)


NaN распространяется при применении к числовой операции. Итак, достаточно проверить окончательный результат на то, является ли он NaN. Что касается того, как это сделать - если сборка для >= C++11, есть std::isnan, как заметил Гоз. Для ‹ С++ 11 - если вы хотите быть пуленепробиваемым - я бы лично провел проверку битов (особенно, если может быть задействована оптимизация). Шаблон для NaN

         ?  11.......1  xx.......x
sign bit ^  ^exponent^  ^fraction^

где ? может быть любым, и хотя бы один x должен быть равен 1.

Для решения, зависящего от платформы, существует еще одна возможность. В glibc есть функция feenableexcept (вероятно, с функцией signal и опцией компилятора -fnon-call-exceptions), которая включает генерацию SIGFPE синалов при возникновении недопустимой операции с плавающей запятой. И функция _control87 (вероятно, с функцией _set_se_translator и опцией компилятора /EHa), которая позволяет почти то же самое в VC.

person Clare    schedule 16.05.2013
comment
На самом деле, именно это распространение усложняет задачу. Что, если вы хотите найти, где это произошло первым, скажем, в 1000 строк кода, разделенных на классы (очевидно, разделяя вычисляемые значения между ними). Вы понимаете ситуацию? Мне было интересно, сталкивался ли кто-нибудь с этой проблемой, и если да, то как они с ней справляются. - person hAcKnRoCk; 16.05.2013
comment
@MhAcKNI, так что, к сожалению, вам приходится полагаться на решение, зависящее от платформы. Как заметил Стивен Кэнон, универсального интерфейса не существует. Или, по крайней мере, я не в курсе. Если вы нацелены на Windows или Unix, попробуйте сделать это выше. - person Clare; 17.05.2013

Хотя это нестандартное расширение исходно из glibc, во многих системах вы можете использовать подпрограмму feenableexcept, объявленную в <fenv.h>, чтобы запросить, чтобы машина перехватывала определенные исключения с плавающей запятой и доставляла SIGFPE вашему процессу. Вы можете использовать fedisableexcept для маскирования перехвата и fegetexcept для запроса набора исключений, которые не маскируются. По умолчанию все они замаскированы.

В более старых системах BSD без этих подпрограмм вы можете вместо этого использовать fpsetmask и fpgetmask из <ieeefp.h>, но мир, кажется, сходится на glibc API.

Предупреждение: в glibc в настоящее время есть ошибка, из-за которой (стандартная процедура C99) fegetenv имеет непреднамеренный побочный эффект маскировки всех ловушек исключений на x86, поэтому вам нужно вызвать fesetenv, чтобы восстановить их позже. (Показывает, насколько сильно кто-то полагается на этот материал...)

person Edmund Wells    schedule 13.06.2013

Во многих архитектурах вы можете демаскировать недопустимое исключение, которое вызовет прерывание, когда NaN обычно генерируется вычислением, таким как 0*infinity. Запустив отладчик, вы остановитесь на этом прерывании и сможете проверить вычисление, которое привело к этому моменту. Помимо отладчика, вы можете установить обработчик прерываний для регистрации информации о состоянии вычислений, вызвавших недопустимую операцию.

Например, на x86 вы должны очистить бит Invalid Operation Mask в FPCR (бит 0) и MXCSR (бит 7), чтобы включить перехват недопустимых операций из операций x87 и SSE соответственно.

Некоторые отдельные платформы предоставляют средства для записи в эти управляющие регистры из C, но не существует переносимого интерфейса, работающего на разных платформах.

person Stephen Canon    schedule 16.05.2013

Тестирование f!=f может вызвать проблемы с использованием g++ с включенной оптимизацией -ffast-math: Проверка, является ли двойное число (или число с плавающей запятой) NaN в C++

Единственный надежный способ - проверить битовый шаблон.

Что касается того, где проводить проверки, это действительно зависит от специфики вашего расчета и от того, насколько часты ошибки Nan, т. Е. Снижение производительности из-за продолжения испорченных вычислений по сравнению с проверкой на определенных этапах.

person OOhay    schedule 16.05.2013