如何在 OS X 中为 clang 启用浮点异常?
How do you enable floating point exceptions for clang in OS X?
我希望我的代码在出现浮点错误时终止。在 linux-gcc 中,"feenableexcept()" 函数可以完成这项工作,但在 OSX 中不可用。在 OS X 上使用 gcc 时,(Enabling floating point interrupts on Mac OS X Intel) 中采用的方法在使用 gcc 时工作得很好,但在使用 clang 时不起作用。
示例代码:
#include <stdio.h>
#include <execinfo.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <iostream>
#include <xmmintrin.h>
void handler(int sig) {
void *array[10];
size_t size;
size = backtrace(array, 10);
fprintf(stderr, "Error: signal %d:\n", sig);
backtrace_symbols_fd(array, size, STDERR_FILENO);
exit(1);
}
int main(int argc, char **argv)
{
_MM_SET_EXCEPTION_MASK( ( _MM_EXCEPT_INVALID |
_MM_EXCEPT_DENORM |
_MM_EXCEPT_DIV_ZERO |
_MM_EXCEPT_OVERFLOW |
_MM_EXCEPT_UNDERFLOW |
_MM_EXCEPT_INEXACT ) );
signal(SIGSEGV, handler);
signal(SIGFPE, handler);
std::cout<<"Perform 1.0/0.0"<<std::endl;
double a = 1.0/0.0;
std::cout<<"1.0/0.0 didn't kill program, result is "<<a<<std::endl<<std::endl;
int* foo = (int*) - 1 ;// make a bad pointer
std::cout<<"Attempting to print a bad pointer"<<std::endl;
printf("%d\n", *foo);
std::cout<<"Bad pointer didn't kill program."<<std::ends;
}
使用gcc5编译时,结果为:
Perform 1.0/0.0
Error: signal 8:
0 a.out 0x000000010f97cb7f _Z7handleri + 28
1 libsystem_platform.dylib 0x00007fff895c652a _sigtramp + 26
2 ??? 0x00007fff6eab6568 0x0 + 140735050114408
3 libdyld.dylib 0x00007fff936a15ad start + 1
太棒了。效果很好。但是,当使用 clang(Apple LLVM 版本 7.3.0 (clang-703.0.29))编译时,结果是这样的:
Perform 1.0/0.0
1.0/0.0 didn't kill program, result is inf
Attempting to print a bad pointer
Error: signal 11:
0 a.out 0x000000010d501d1f _Z7handleri + 31
1 libsystem_platform.dylib 0x00007fff895c652a _sigtramp + 26
2 ??? 0x00007fff62b7e568 0x0 + 140734849607016
3 libdyld.dylib 0x00007fff936a15ad start + 1
不太好。没有提高 FPE,代码一直在继续。我环顾四周,找不到如何让 clang 提高 FPE。有人对此有经验吗?谢谢!
您设置了屏蔽异常。所以你必须清除位,如果你想得到异常:
_MM_SET_EXCEPTION_MASK( _MM_GET_EXCEPTION_MASK()
& ~( _MM_EXCEPT_INVALID |
_MM_EXCEPT_DENORM |
_MM_EXCEPT_DIV_ZERO |
_MM_EXCEPT_OVERFLOW |
_MM_EXCEPT_UNDERFLOW |
_MM_EXCEPT_INEXACT ) );
公认的方法似乎是 header 项目 built-in 包含的新方法。对于 Linux,此文件将被忽略并简单地回退到库存 fenv.h
。
此代码来自 2009, David N. Williams
,从 ardupilot
项目中逐字借用,这是我能找到的第一个结果。我可以使用 Apple LLVM version 9.0.0 (clang-900.0.39.2
).
确认这适用于 MacOS 10.13
注意: 注意,尽管我看到的许多建议都建议将此唯一命名为 header 为了清楚起见,但在很多方面它让我想起了 polyfill
(来自 JS/webdev 的术语),因此模仿该意识形态,从中借用代码的项目以及我自己的项目都保留了它的名称 fenv.h
。根据需要重命名。
fenv.h
#pragma once
#include_next <fenv.h>
#if defined(__APPLE__) && defined(__MACH__)
// Public domain polyfill for feenableexcept on OS X
// http://www-personal.umich.edu/~williams/archive/computation/fe-handling-example.c
inline int feenableexcept(unsigned int excepts)
{
static fenv_t fenv;
unsigned int new_excepts = excepts & FE_ALL_EXCEPT;
// previous masks
unsigned int old_excepts;
if (fegetenv(&fenv)) {
return -1;
}
old_excepts = fenv.__control & FE_ALL_EXCEPT;
// unmask
fenv.__control &= ~new_excepts;
fenv.__mxcsr &= ~(new_excepts << 7);
return fesetenv(&fenv) ? -1 : old_excepts;
}
inline int fedisableexcept(unsigned int excepts)
{
static fenv_t fenv;
unsigned int new_excepts = excepts & FE_ALL_EXCEPT;
// all previous masks
unsigned int old_excepts;
if (fegetenv(&fenv)) {
return -1;
}
old_excepts = fenv.__control & FE_ALL_EXCEPT;
// mask
fenv.__control |= new_excepts;
fenv.__mxcsr |= new_excepts << 7;
return fesetenv(&fenv) ? -1 : old_excepts;
}
#endif
我希望我的代码在出现浮点错误时终止。在 linux-gcc 中,"feenableexcept()" 函数可以完成这项工作,但在 OSX 中不可用。在 OS X 上使用 gcc 时,(Enabling floating point interrupts on Mac OS X Intel) 中采用的方法在使用 gcc 时工作得很好,但在使用 clang 时不起作用。
示例代码:
#include <stdio.h>
#include <execinfo.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <iostream>
#include <xmmintrin.h>
void handler(int sig) {
void *array[10];
size_t size;
size = backtrace(array, 10);
fprintf(stderr, "Error: signal %d:\n", sig);
backtrace_symbols_fd(array, size, STDERR_FILENO);
exit(1);
}
int main(int argc, char **argv)
{
_MM_SET_EXCEPTION_MASK( ( _MM_EXCEPT_INVALID |
_MM_EXCEPT_DENORM |
_MM_EXCEPT_DIV_ZERO |
_MM_EXCEPT_OVERFLOW |
_MM_EXCEPT_UNDERFLOW |
_MM_EXCEPT_INEXACT ) );
signal(SIGSEGV, handler);
signal(SIGFPE, handler);
std::cout<<"Perform 1.0/0.0"<<std::endl;
double a = 1.0/0.0;
std::cout<<"1.0/0.0 didn't kill program, result is "<<a<<std::endl<<std::endl;
int* foo = (int*) - 1 ;// make a bad pointer
std::cout<<"Attempting to print a bad pointer"<<std::endl;
printf("%d\n", *foo);
std::cout<<"Bad pointer didn't kill program."<<std::ends;
}
使用gcc5编译时,结果为:
Perform 1.0/0.0
Error: signal 8:
0 a.out 0x000000010f97cb7f _Z7handleri + 28
1 libsystem_platform.dylib 0x00007fff895c652a _sigtramp + 26
2 ??? 0x00007fff6eab6568 0x0 + 140735050114408
3 libdyld.dylib 0x00007fff936a15ad start + 1
太棒了。效果很好。但是,当使用 clang(Apple LLVM 版本 7.3.0 (clang-703.0.29))编译时,结果是这样的:
Perform 1.0/0.0
1.0/0.0 didn't kill program, result is inf
Attempting to print a bad pointer
Error: signal 11:
0 a.out 0x000000010d501d1f _Z7handleri + 31
1 libsystem_platform.dylib 0x00007fff895c652a _sigtramp + 26
2 ??? 0x00007fff62b7e568 0x0 + 140734849607016
3 libdyld.dylib 0x00007fff936a15ad start + 1
不太好。没有提高 FPE,代码一直在继续。我环顾四周,找不到如何让 clang 提高 FPE。有人对此有经验吗?谢谢!
您设置了屏蔽异常。所以你必须清除位,如果你想得到异常:
_MM_SET_EXCEPTION_MASK( _MM_GET_EXCEPTION_MASK()
& ~( _MM_EXCEPT_INVALID |
_MM_EXCEPT_DENORM |
_MM_EXCEPT_DIV_ZERO |
_MM_EXCEPT_OVERFLOW |
_MM_EXCEPT_UNDERFLOW |
_MM_EXCEPT_INEXACT ) );
公认的方法似乎是 header 项目 built-in 包含的新方法。对于 Linux,此文件将被忽略并简单地回退到库存 fenv.h
。
此代码来自 2009, David N. Williams
,从 ardupilot
项目中逐字借用,这是我能找到的第一个结果。我可以使用 Apple LLVM version 9.0.0 (clang-900.0.39.2
).
注意: 注意,尽管我看到的许多建议都建议将此唯一命名为 header 为了清楚起见,但在很多方面它让我想起了 polyfill
(来自 JS/webdev 的术语),因此模仿该意识形态,从中借用代码的项目以及我自己的项目都保留了它的名称 fenv.h
。根据需要重命名。
fenv.h
#pragma once
#include_next <fenv.h>
#if defined(__APPLE__) && defined(__MACH__)
// Public domain polyfill for feenableexcept on OS X
// http://www-personal.umich.edu/~williams/archive/computation/fe-handling-example.c
inline int feenableexcept(unsigned int excepts)
{
static fenv_t fenv;
unsigned int new_excepts = excepts & FE_ALL_EXCEPT;
// previous masks
unsigned int old_excepts;
if (fegetenv(&fenv)) {
return -1;
}
old_excepts = fenv.__control & FE_ALL_EXCEPT;
// unmask
fenv.__control &= ~new_excepts;
fenv.__mxcsr &= ~(new_excepts << 7);
return fesetenv(&fenv) ? -1 : old_excepts;
}
inline int fedisableexcept(unsigned int excepts)
{
static fenv_t fenv;
unsigned int new_excepts = excepts & FE_ALL_EXCEPT;
// all previous masks
unsigned int old_excepts;
if (fegetenv(&fenv)) {
return -1;
}
old_excepts = fenv.__control & FE_ALL_EXCEPT;
// mask
fenv.__control |= new_excepts;
fenv.__mxcsr |= new_excepts << 7;
return fesetenv(&fenv) ? -1 : old_excepts;
}
#endif