Return from catching floating point exceptions

So, I am trying to return from a floating point exception, but my code continues the loop. I can actually exit the process, but what I want to do is return and repeat the calculation that causes the floating point error.

The reason for the occurrence of FPE is because I have a random number generator that generates coefficients for the polynomial. Using some of the LAPACK functions, I solve for the roots and do some other things. Somewhere in this intense mathematical chain, a floating point exception is thrown. When this happens, what I want to do is increase the state of the random number generator and try again until the coefficients are such that the error materializes, as it usually happens, but very rarely and leads to disastrous results.

So, I wrote a simple test program to learn how to work with signals. It is below:

In 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 // 

In exceptions.c

 #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; } 

In 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; } 

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 

Why doesn't this program end when I clear the exceptions, supposedly successfully? What do I need to do to return to the main thing and exit?

If I can do this, I can put the code between the return and the exit and do something after the FPE has been caught. I think I will set some kind of flag, and then clear all the latest information in the data structures, repeat the calculation, etc. Based on whether this flag is set. The fact is that the real program should not be interrupted, but the cycle should be forever, but instead should handle the exception and continue to work.

reference

+5
source share
2 answers

I think you should bother with the frame of the calling stack if you want to skip the instruction or exit exp or something else. This is high voodoo and inevitably incapable.

The GNU C library allows you to use setjmp() outside of the signal handler, with which you can longjmp() internally. This seems like the best way. The following is a separate modification of your program, showing how to do this:

 #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; } 
+3
source

"division by zero", overflow / underflow, etc. lead to undefined behavior in the first place. If the system, however, generates a signal for this, the UB effect โ€œpausesโ€. Instead, a signal handler is executed. But if the handler returns, the UB effect will โ€œresumeโ€.

Therefore, the standard prohibits returning from such a situation.

Think about how the program should recover, for example. Div0? An abstract machine has no idea about FPU registers or status flags, and even if - what result should be generated?

C also has no provisions to properly open the stack, like C ++.

We also note that the generation of signals for arithmetic exceptions is optional, therefore there is no guarantee that the signal will actually be generated. The handler is mainly intended for event notification and, possibly, for cleaning up external resources.

The behavior is different for signals that do not come from undefined behavior, but simply interrupt program execution. This is well defined, as the state of the program is clearly defined.

Edit:

If you need to rely on a program to continue under any circumstances, you should check all the arguments of arithmetic operations before performing the actual operation and / or use only safe operations (reorder, use larger intermediate types, etc.). One example for integers might be to use unsigned instead of signed integers, as for those overflow operations are correctly defined (wraps), so the intermediate overflow results will not cause problems if this is fixed later and the wrapper is not too much. (Disclaimer: this does not always work, of course).

Update:

Although Iโ€™m still not quite sure, according to the comments, the standard may allow, at least for a hosted environment, to use LIA-1 traps and restore them (see Appendix H. Since this is not necessarily accurate, I suspect that recovery is not possible with any In addition, math.h may present additional aspects that need to be carefully evaluated.

Finally: I still think that with this approach nothing works, but some uncertainty has been added compared to using safe algorithms. It would be different if not many different components were involved. For an embedded system with white metal, the appearance may be completely different.

+4
source

All Articles