Страницы

Поиск по вопросам

среда, 5 февраля 2020 г.

Сигнал SIGFPE вместо NaN

#cpp #linux #c #signal


В некоторых linux системах вместо исключения в операции с плавающей точкой (сигнал
SIGFPE) появляется значение NaN и вероятность отыскать ошибку близка к нулю и о существовании
ошибки можно лишь догадываться, когда в конце появилось NaN. Хотелось бы, чтобы такое
проблемное место убивало программу (тогда отладчик может помочь отыскать проблемное
место). Собственно говоря, суть вопроса в том, как сделать, что бы ошибка в операции
с плавающей точкой приводила к SIGFPE.
    


Ответы

Ответ 1



В реализации libc от GNU есть нестандартная функция feenableexcept(). Она же есть и во многих других libc (в том числе {Free,Open}BSD и uclibc). В качестве аргумента она принимает битовую маску исключительных ситуаций (аналогично fesetexceptflag() и др.): FE_DIVBYZERO — деление на ноль FE_OVERFLOW — переполнение FE_UNDERFLOW — обратное переполнение FE_INEXACT — неточный результат FE_INVALID — неверная операция Все эти макросы оопределены только если платформа их поддерживает. Также определён макрос FE_ALL_EXCEPT, который представляет комбинацию всех вышеописанных. Также для отмены сигнала есть fedisableexcept(). Как и многие другие расширения GNU, они требует определения features-test макроса _GNU_SOURCE до включения каких-либо системных хедеров. К сожалению, в Linux man-pages обе эти функции слабо документированы: о ней упомянуто только вскользь в man fenv. , так что за какими-то подробностями лучше обратиться к исходникам и/или к справке из других ОС. К сожалению, после получения SIGFPE восстановить нормальную работу непосредственно с точки возникновения сигнала невозможно, так что останется только завершиться (или сделать longjmp(), см. комментарии). Так что, пожалуй, данные функции ограничены к использованию только отладкой. Возврат из обработчика сигнала приведёт к зацикливанию т.к. выполнение продолжится с той же инструкции, которая вызвала ошибку. Как это работает Это функции работает без какоой-либо магии со стороны компилятора или системы. Всё завязано исключительно на возможности процессора. На x86 просто устанавливаются соответствующие биты в управляющем регистре CW для x87 и, при возможности, в MXCSR для SSE (см. FLDCW и LDMXCSR соответственно). Далее при определении соответствующей ситуации процессор просто выкинет исключение, которое обработает ядро и отправит сигнал текущему процессу. Пример Подсказка: дабы получить разные ошибки, нужно раскомментировать строки в конце: #define _GNU_SOURCE #include #include #include #include #include #include #define BUF_SZ 1024 void sigfpe_hdl(int signum, siginfo_t *si, void *ctx) { if (signum == SIGFPE) { char buf[BUF_SZ]; char *msg; switch (si->si_code) { /* see `man 2 sigaction` for the list */ case FPE_INTDIV: msg = "Integer divide by zero" ; break; case FPE_INTOVF: msg = "Integer overflow" ; break; case FPE_FLTDIV: msg = "Floating-point divide by zero" ; break; case FPE_FLTOVF: msg = "Floating-point overflow" ; break; case FPE_FLTUND: msg = "Floating-point underflow" ; break; case FPE_FLTRES: msg = "Floating-point inexact result" ; break; case FPE_FLTINV: msg = "Floating-point invalid operation"; break; case FPE_FLTSUB: msg = "Subscript out of range" ; break; default: msg = "Unknown FPE"; } char *msgend=buf; msgend = stpncpy (msgend,"Received SIGFPE. Reason: ", BUF_SZ - (msgend-buf)); msgend = stpncpy (msgend, msg, BUF_SZ - (msgend-buf)); msgend = stpncpy (msgend, ".\n", BUF_SZ - (msgend-buf)); write (STDERR_FILENO, buf, msgend - buf); } exit (EXIT_FAILURE); } int main() { struct sigaction sa_fpe = { .sa_sigaction = sigfpe_hdl, .sa_flags = SA_SIGINFO, }; sigaction (SIGFPE, &sa_fpe, 0); feenableexcept (FE_ALL_EXCEPT); int i,j; // i=10; j=0; i=i/j; // integer divide by zero float x, y; // x = 10.0; y= 0.0; x = x/y; // divide by zero // x = 1e+25; y= 1e+25; x = x*y; // overflow // x = 1e-25; y= 1e-25; x = x*y; // underflow // x = 0.2; y= 0.1; x = x+y; // inexact result // x = 0.0; y= 0.0; x = x/y; // invalid operation }

Комментариев нет:

Отправить комментарий