【问题标题】:C++ setting floating point exception environmentC++设置浮点异常环境
【发布时间】:2020-03-18 08:27:30
【问题描述】:

我正在努力尝试以便携式方式设置 std::fenv。

基于this cppreference 页面,fesetexceptflag(const std::fexcept_t*,int) 似乎应该帮助我解决问题。另一方面,我发现 GNU 也提供了feenableexcept(int) 函数。我的理解是 feenablexcept 是 GNU 特定的,虽然我非常可能总是可以访问 GNU 的东西,但我希望只使用 std 的东西,也就是说,坚持使用 @987654325 @。 我写了一个小测试,发现feenableexcept 方法有效,而fesetexceptflag 方法无效。这是两个例子。交换main开头两行的cmets,得到版本1(fesetexceptflag)和版本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;
}

版本 1 输出:

current mask: 4
mask is FE_DIVBYZERO: yes
All done!

版本 2 输出:

current mask: 0
mask is FE_DIVBYZERO: no
signal 8 caught.

因此,版本 1 似乎在 fenv 中正确设置了异常标志,但未能引发 SIGFPE,而版本 2 没有设置异常标志,但确实引发了 SIGFPE。这里发生了什么事?我是否误解了fesetexceptflag 的文档?我的理解是,它会抓取第一个 arg 中在第二个 arg 中处于活动状态的所有位,并将它们放入 fenv 中(这似乎正在发生)。但是,它似乎无效。另一方面,版本 2 具有 0 掩码 fenv,但成功提高了 SIGFPE。我很困惑。

如果有帮助的话,我正在一台 linux 机器 (Red Hat) 上使用 gcc 8.2.0。

【问题讨论】:

    标签: c++ gcc sigfpe fenv


    【解决方案1】:

    我是否误解了 fesetexceptflag 的文档?

    是的。 fesetexceptflag 表示:设置此异常标志以表示已报告异常。

    fetestexcept 的正确用法是:

    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
    

    没有标准方法可以让浮点异常引发信号。这就是 glibc 为您提供的功能。不过,您可以自己发出信号:

    if (fetestexcept(FE_ALL_EXCEPT))
        raise(SIGFPE);
    

    【讨论】:

    • 啊,所以fetestexcept 不测试“会引发什么异常”,而是测试“已经引发了什么”。对吗?
    【解决方案2】:

    我是否误解了 fesetexceptflag 的文档?

    是的。例如,当您执行 1.0/0 时,FE_DIVBYZERO 标志 gets 在当前环境中引发。 fesetexceptflag 不允许您决定当您除以 0 时会发生什么fesetexceptflag 让您检查 如果您已经执行的操作 em> 导致异常。

    您只能使用math_errhandling 宏查看浮点异常会发生什么。这是一个宏,只说明使用了errno 或浮点异常。

    这个小例子可以提供一些启示:

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

    将输出:

    no FE_DIVBYZERO
    FE_DIVBYZERO
    

    使用fexcept_t,您可以恢复当前设置标志的实现定义表示。你不能做std::fexcept_t my_flag = FE_DIVBYZERO; - 好吧,你可以,但是fexcept_t 的内容是实现定义的,所以结果将是实现定义的。您不能手动修改fexcept_t。您只能使用fegetexceptflag 保存当前引发的浮点异常,进行一些可以抛出您想要检查的计算,然后使用具有相同标志的fesetexceptflag 恢复浮点异常。您将fexcept_t 修改为sesetexceptflag,然后使用feclearexceptferaiseexcept,然后使用fegetexceptflag 保存。

    抛出SIGFPE 信号是扩展C99 J.5.17p1。它可能会附加或代替设置 errno 或浮点标志发生。扩展是使用 gnu 函数feenableexcept 激活的。

    【讨论】:

      猜你喜欢
      • 2021-07-16
      • 1970-01-01
      • 2010-09-10
      • 2010-12-17
      • 1970-01-01
      • 2011-02-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多