3

我希望我的代码在出现浮点错误时终止。在 linux-gcc 中,“feenableexcept()”函数完成了这项工作,但这在 OSX 中不可用。在 OS X 上使用 gcc 时,采用的方法(在 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,代码只是一直在运行。我环顾四周,找不到如何让铿锵声来提高 FPE。有人对此有经验吗?谢谢!

4

2 回答 2

1

接受的方法似乎是项目内置包含的新标题。对于 Linux,此文件将被忽略并简单地回退到 stock fenv.h.

这段代码是 credit 2009, David N. Williams,逐字从ardupilot项目中借来的,这是我能找到的第一个结果。我可以用 ) 确认这适用于 MacOS 10.13 Apple LLVM version 9.0.0 (clang-900.0.39.2

注意: 注意,虽然我看到的许多建议为了清楚起见建议对这个标头进行唯一命名,但在许多方面它让我想起了一个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
于 2018-03-05T02:49:42.380 回答
0

您设置了屏蔽的异常。因此,如果要获取异常,则必须清除该位:

_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 ) );
于 2016-06-15T05:49:34.527 回答