I'm struggling trying to set the std::fenv in a portable way.
Based on this cppreference page, it seems that fesetexceptflag(const std::fexcept_t*,int)
should help me do the trick. On the other hand, I found out that GNU offers also the feenableexcept(int)
function. My understanding is that feenablexcept
is GNU specific, and, although I'm highly likely to always have access to GNU stuff, I was hoping to just use std stuff, that is, stick with fesetexceptflag
. I wrote a little test, and found out that the feenableexcept
approach works, while the fesetexceptflag
one doesn't. Here are the two examples. Swap comments in the two lines at the beginning of main to obtain version 1 ( fesetexceptflag
) and version 2 ( feenableexcept
):
#include <cfenv>
#include <csignal>
#include <cstdio>
void signal_handler (int signum) {
printf ("signal %d caught.\n",signum);
exit(1);
}
int main(int,char**){
std::fexcept_t my_flag = FE_DIVBYZERO;
fesetexceptflag(&my_flag,FE_ALL_EXCEPT); // Uncomment this for version 1
// feenableexcept(my_flag); // Uncomment this for version 2
int mask = fetestexcept(FE_ALL_EXCEPT)
printf("current mask: %d\n",mask);
printf("mask is FE_DIVBYZERO: %s\n",mask==FE_DIVBYZERO ? "yes" : "no");
signal(SIGFPE, signal_handler);
double one = 1.0;
double zero = 0.0;
double my_inf = one/zero;
printf("All done!\n");
return 0;
}
Version 1 output:
current mask: 4
mask is FE_DIVBYZERO: yes
All done!
Version 2 output:
current mask: 0
mask is FE_DIVBYZERO: no
signal 8 caught.
So it seems that version 1 sets the exception flags correctly in the fenv, but fails to raise SIGFPE, while version 2 does not set the exception flags, but does raise SIGFPE. What's happening here? Am I misinterpreting the documentation of fesetexceptflag
? My understanding was that it grabs all bits in the first arg that are active in the second arg, and puts them in the fenv (which is what seems to happen). But then, it seems ineffective. On the other hand, version 2 has a 0 mask fenv, and yet succeeds in raising SIGFPE. I'm very confused.
I'm using gcc 8.2.0 on a linux machine (Red Hat), if that can help.
Am I misinterpreting the documentation of fesetexceptflag?
Yes. fesetexceptflag
means: Set this exception flag to signify that an exception has been reported.
A correct usage of fetestexcept
is:
feclearexcept(FE_ALL_EXCEPT);
int mask = fetestexcept(FE_ALL_EXCEPT);
printf("current mask: %d\n",mask);
printf("FE_DIVBYZERO before: %s\n",std::fetestexcept(FE_DIVBYZERO) ? "yes" : "no"); // no
double my_inf = one/zero;
int mask = fetestexcept(FE_ALL_EXCEPT);
printf("current mask: %d\n",mask);
printf("FE_DIVBYZERO after: %s\n",std::fetestexcept(FE_DIVBYZERO) ? "yes" : "no"); // yes
There is no standard way to make floating point exceptions raise a signal. That is the feature that glibc provides you. You can raise the signal yourself though:
if (fetestexcept(FE_ALL_EXCEPT))
raise(SIGFPE);
Am I misinterpreting the documentation of fesetexceptflag?
Yes. When you do for example 1.0/0
then the FE_DIVBYZERO
flag gets raised in the current environment. fesetexceptflag
doesn't let you decide what will happen when you divide by 0. fesetexceptflag
let's you check if the operation that you already did resulted in exception.
You can only see what will happen on floating-point exceptions withmath_errhandling macro. Which is a macro that only tells that either errno
or floating point exceptions are used.
This little example could shed some light:
#include <cfenv>
#include <cstdio>
#pragma STDC FENV_ACCESS ON
#if math_errhandling != MATH_ERREXCEPT
#error This code needs to use floating point exceptions
#endif
int main() {
std::feclearexcept(FE_ALL_EXCEPT);
printf("%s\n", std::fetestexcept(FE_DIVBYZERO) ? "FE_DIVBYZERO" : "no FE_DIVBYZERO");
double a = 1.0/0;
printf("%s\n", std::fetestexcept(FE_DIVBYZERO) ? "FE_DIVBYZERO" : "no FE_DIVBYZERO");
}
will output:
no FE_DIVBYZERO
FE_DIVBYZERO
With fexcept_t
you can restore implementation defined representation of currently set flags. You can't do std::fexcept_t my_flag = FE_DIVBYZERO;
- well, you can, but the content of fexcept_t
is implementation defined, so the result will be implementation-defined. You can't modify fexcept_t
manually. You can only save currently raised floating point exceptions with fegetexceptflag
, do some calculations that can throw that you want to inspect, and then restore the floating point exceptions with fesetexceptflag
with the same flag. You modify fexcept_t
by sesetexceptflag
then with feclearexcept
and feraiseexcept
and then saving it with fegetexceptflag
.
Throwing a SIGFPE
signal is an extension C99 J.5.17p1 . It could happen in addition or instead of setting errno or floating point flags. The extension is activated with the gnu function feenableexcept
.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.