从捕获浮点异常返回

The*_*ude 5 c floating-point signals exception-handling

所以,我试图从浮点异常返回,但我的代码继续循环.我实际上可以退出该过程,但我想要做的是返回并重做导致浮点错误的计算.

发生FPE的原因是因为我有一个随机数生成器,它为多项式生成系数.使用一些LAPACK函数,我解决了根源并做了一些其他事情.在此数学密集型链中的某处,会发生浮点异常.当发生这种情况时,我想要做的是递增随机数生成器状态,并再次尝试直到系数使得错误没有实现,因为它通常不会,但很少会导致灾难性的结果.

所以我写了一个简单的测试程序来学习如何处理信号.它在下面:

在exceptions.h中

#ifndef EXCEPTIONS_H
#define EXCEPTIONS_H

#define _GNU_SOURCE

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <math.h>
#include <errno.h>
#include <float.h>
#include <fenv.h>

void overflow_handler(int);

#endif // EXCEPTIONS_H //
Run Code Online (Sandbox Code Playgroud)

在例外情况下

#include "exceptions.h"

void overflow_handler(int signal_number)
{
    if (feclearexcept(FE_OVERFLOW | FE_UNDERFLOW | FE_DIVBYZERO | FE_INVALID)){
        fprintf(stdout, "Nothing Cleared!\n");
    }
    else{
        fprintf(stdout, "All Cleared!\n");
    }

    return;
}
Run Code Online (Sandbox Code Playgroud)

在main.c

#include "exceptions.h"


int main(void)
{
    int failure;
    float oops;

    //===Enable Exceptions===//
    failure = 1;
    failure = feenableexcept(FE_OVERFLOW | FE_UNDERFLOW | FE_DIVBYZERO | FE_INVALID);
    if (failure){
        fprintf(stdout, "FE ENABLE EXCEPTIONS FAILED!\n");
    }

    //===Create Error Handler===//
    signal(SIGFPE, overflow_handler);

    //===Raise Exception===//
    oops = exp(-708.5);
    fprintf(stdout, "Oops: %f\n", oops);

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

Makefile

#===General Variables===#
CC=gcc
CFLAGS=-Wall -Wextra -g3 -Ofast

#===The Rules===#
all: makeAll

makeAll: makeExceptions makeMain
    $(CC) $(CFLAGS) exceptions.o main.o -o exceptions -ldl -lm

makeMain: main.c 
    $(CC) $(CFLAGS) -c main.c -o main.o

makeExceptions: exceptions.c exceptions.h
    $(CC) $(CFLAGS) -c exceptions.c -o exceptions.o 

.PHONY: clean

clean:
    rm -f *~ *.o
Run Code Online (Sandbox Code Playgroud)

当我清除异常时,为什么这个程序不会终止?我需要做什么才能返回主站并退出?

如果我能做到这一点,我可以在返回和退出之间放置代码,并在FPE被捕获后执行某些操作.我想我会设置某种标志,然后清除数据结构中的所有最新信息,根据是否设置该标志重做计算等.关键是,真正的程序不能中止也不能永远循环,而是必须处理异常并继续前进.

救命?

tmy*_*ebu 3

我认为如果你想跳过指令或中断exp或其他什么,你应该搞乱调用堆栈帧。这是一种高深的巫毒,注定是不可移植的。

GNU C 库允许您setjmp()在信号处理程序外部使用,而您可以longjmp()从内部使用该信号处理程序。这似乎是一个更好的方法。这是对程序的独立修改,展示了如何执行此操作:

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <setjmp.h>
#include <math.h>
#include <errno.h>
#include <float.h>
#include <fenv.h>

sigjmp_buf oh_snap;

void overflow_handler(int signal_number) {
    if (feclearexcept(FE_OVERFLOW | FE_UNDERFLOW | FE_DIVBYZERO | FE_INVALID)){
        fprintf(stdout, "Nothing Cleared!\n");
    }
    else{
        fprintf(stdout, "All Cleared!\n");
    }
    siglongjmp(oh_snap, 1);

    return;
}

int main(void) {
    int failure;
    float oops;
    failure = 1;
    failure = feenableexcept(FE_OVERFLOW | FE_UNDERFLOW | FE_DIVBYZERO | FE_INVALID);
    if (failure){
        fprintf(stdout, "FE ENABLE EXCEPTIONS FAILED!\n");
    }
    signal(SIGFPE, overflow_handler);
    if (sigsetjmp(oh_snap, 1)) {
      printf("Oh snap!\n");
    } else {
      oops = exp(-708.5);
      fprintf(stdout, "Oops: %f\n", oops);
    }
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

  • 您可能应该使用“sigsetjmp/siglongjmp”。 (2认同)
  • 当使用“signal()”时,任何人都可以猜测(BSD 与 sysV)。BSD 行为应该是在处理程序执行时阻止信号。由于你再也没有回来,信号可能永远不会畅通。您还可以通过“sigprocmask()”解锁它 (2认同)