The way to trap floating-point exceptions is architecture-dependent. This is code I have tested successfully on an Intel (x86) Mac: it takes the square root of a negative number twice, once before, and once after, enabling floating-point exception trapping. The second time, fpe_signal_handler()
is called.
#include <cmath> // for sqrt()
#include <csignal> // for signal()
#include <iostream>
#include <xmmintrin.h> // for _mm_setcsr
void fpe_signal_handler(int /*signal*/) {
std::cerr << "Floating point exception!\n";
exit(1);
}
void enable_floating_point_exceptions() {
_mm_setcsr(_MM_MASK_MASK & ~_MM_MASK_INVALID);
signal(SIGFPE, fpe_signal_handler);
}
int main() {
const double x{-1.0};
std::cout << sqrt(x) << "\n";
enable_floating_point_exceptions();
std::cout << sqrt(x) << "\n";
}
Compiling with the apple-clang compiler provided by Xcode
clang++ -g -std=c++17 -o fpe fpe.cpp
and running gives the following expected output:
nan
Floating point exception!
I would like to write an analogous program that does the same thing as the above program on an M1 (arm64) Mac. I tried the following:
#include <cfenv> // for std::fenv_t
#include <cmath> // for sqrt()
#include <csignal> // for signal()
#include <fenv.h> // for fegetenv(), fesetenv()
#include <iostream>
void fpe_signal_handler(int /*signal*/) {
std::cerr << "Floating point exception!\n";
exit(1);
}
void enable_floating_point_exceptions() {
std::fenv_t env;
fegetenv(&env);
env.__fpcr = env.__fpcr | __fpcr_trap_invalid;
fesetenv(&env);
signal(SIGFPE, fpe_signal_handler);
}
int main() {
const double x{-1.0};
std::cout << sqrt(x) << "\n";
enable_floating_point_exceptions();
std::cout << sqrt(x) << "\n";
}
It almost works: After compiling with the apple-clang compiler provided by Xcode
clang++ -g -std=c++17 -o fpe fpe.cpp
I get the following output:
nan
zsh: illegal hardware instruction ./fpe
I tried adding the -fexceptions
flag, but that didn't make a difference. I noticed that the ARM Compiler toolchain "does not support floating-point exception trapping for AArch64 targets," but I'm not sure if this applies to M1 Macs with Apple's toolchain.
Am I correct that the M1 Mac hardware just doesn't support floating-point exception trapping? Or is there a way to modify this program so it traps the second floating-point exception and then calls fpe_signal_handler()
?
Synchronously testing for exceptions within the same thread does work fine, using ISO C fetestexcept
from <fenv.h>
as in the cppreference example. The problem here is getting FP exceptions to actually trap so the OS delivers SIGFPE
, instead of just setting sticky flags in the FP environment.