В некоторых linux системах вместо исключения в операции с плавающей точкой (сигнал SIGFPE) появляется значение NaN и вероятность отыскать ошибку близка к нулю и о существовании ошибки можно лишь догадываться, когда в конце появилось NaN. Хотелось бы, чтобы такое проблемное место убивало программу (тогда отладчик может помочь отыскать проблемное место). Собственно говоря, суть вопроса в том, как сделать, что бы ошибка в операции с плавающей точкой приводила к SIGFPE.
В реализации 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fenv.h>
#include <unistd.h>
#include <signal.h>
#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
}
¹ Аналогичного результата можно добиться с помощью стандартных fegetenv()+fesetexceptflag()+fsetenv().
Сборка персонального компьютера от Artline: умный выбор для современных пользователей