在混合 C/Fortran 代码中捕获浮点异常

Posted

技术标签:

【中文标题】在混合 C/Fortran 代码中捕获浮点异常【英文标题】:Trapping floating point exceptions in mixed C/Fortran code 【发布时间】:2013-07-04 14:54:27 【问题描述】:

我很抱歉问了一个以前被问过很多次的问题。但是经过几次搜索,我意识到我可能对如何在 C/C++ 中处理 FPE 与如何在 Fortran 中处理它们之间存在根本性的误解。

在 Fortran(准确地说是 GNU fortran)中,如果想要捕获浮点异常(例如使用 NAN),编译器标志 -ffpe-trap=invalid 可以解决问题。一旦执行了违规语句,就会引发浮点异常。

然而,在 C (GNU gcc) 中,情况似乎并非如此。更烦人(但也许并不奇怪)是相同的 fortran 代码,当从 C 主程序调用时不会引发异常(并停止执行),而从 Fortran 主程序调用时会引发异常。这似乎与是否使用 C 或 gfortran 链接器无关。

经过大量搜索和阅读,我发现 fenv.h 中提供了 C/C++ 功能,这表明了处理异常的“C 方式”。我看到我可以设置异常标志,然后检查是否引发了异常。我可以看到这种方法如何为异常处理方式提供更多灵活性。这是在 C 中处理异常的“最佳实践”方式吗?对于科学编程(C 通常用于调用 fortran 代码),必须对异常可能发生的位置有一些高级知识似乎很不方便。

C 中是否没有(直接的)方法可以让代码在第一次出现异常时停止?或者当涉及到 C 中的异常处理时,我还没有完全掌握另一种范式吗?

【问题讨论】:

原来feenableexcept() 是非标准的。 this question 的答案有一些建议,this blog post 指出了一些适用于 linux 和 Mac OS X 的代码。 是的,我发现了这篇文章,并且引用的代码似乎完全符合我的期望。您是否知道所描述的扩展是否是一种合理的方法? 我还应该评论说,我寻找捕获异常的方法的主要目的是调试 fortran 代码中未初始化数据的使用。我的计划是使用 NaN 值初始化数据,然后在以后捕获这些值的使用。 我投了赞成票只是因为这个问题得到了很好的解释并且提问者知道他在寻找什么...... 【参考方案1】:

由于您使用 GNU 实用程序,我假设您使用的是 *nix。您需要启用浮点异常。完成后,使用信号传递异常。下面的代码说明了这一点:

#include <fenv.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>

void handler(int sig) 
    printf("Floating Point Exception\n");
    exit(0);



int main(int argc, char ** argv) 
    feenableexcept(FE_INVALID | FE_DIVBYZERO | FE_OVERFLOW | FE_UNDERFLOW);
    signal(SIGFPE, handler);

    float a = 42.0, b = 0.0, res;

    res = a / b;

    return 0;

与 libm 的链接:

gcc -o test test.c -lm

在 Windows 上,我相信您需要使用结构化异常处理程序:http://msdn.microsoft.com/en-us/library/windows/desktop/ms680657(v=vs.85).aspx

【讨论】:

我正在运行 OS X,但无法使用feenableexcept。但我确实遇到了这个扩展,结果证明它正是我想要的。 OSX_fpe_fix【参考方案2】:

在下面的代码中,我展示了如何完全按照我的目标来处理上面的问题。它依赖于提到的 Mac OSX 扩展 here 和所描述的信号值 here。

我不是这两个主题的专家,因此无法断言此代码的可移植性。但它确实做了我想要的两件事:它允许我将数据初始化为“NAN”,然后捕获这些未初始化值的无效使用。正常执行和 gdb 都会检测到陷阱。

我当然会感谢任何有关此解决方案的 cmets。


#include "fp_exception_glibc_extension.h"
#include <fenv.h>
#include <signal.h>
#include <stdio.h>

/*
-----------------------------------------------------------------------
This example illustrates how to initialize data with an sNAN.  Later, if
the data is used in its 'uninitialized' state, an exception is raised,
and execution halts.

The floating point exception handler 'fp_exception_glibc_extension' is
 needed for OS X portability.

 At least two blog posts were used in writing this :

 "Update: Floating-point exception handling on Mac OS X"
 http://philbull.wordpress.com/2012/12/09/update-floating-point-exception-handling-on-mac-os-x/
 Note : there is a lengthy email exchange about how portable this extension is;  see
 comments in the text of the code.

 "NaNs, Uninitialized Variables, and C++"
 http://codingcastles.blogspot.fr/2008/12/nans-in-c.html
 -----------------------------------------------------------------------
*/

void set_snan(double& f)

    *((long long*)&f) = 0x7ff0000000000001LL;


int main()

    /* On OS X, this extension is provided by 
       'fp_exception_glibc_extension.c,h */
    feenableexcept(FE_INVALID);

    double p;
    set_snan(p); /* Initialize to signaling nan */

    double q;
    q = 2*p;     /* Floating point exception is trapped here */
    printf("p = %f; q = %f\n",p,q);

【讨论】:

这里需要使用'set_snan'。稍后在程序中使用时,不会捕获到 math.h 中提供的 NAN 宏的初始化值。

以上是关于在混合 C/Fortran 代码中捕获浮点异常的主要内容,如果未能解决你的问题,请参考以下文章

C#:捕获混合托管/非托管进程的所有错误/异常

FORTRAN & MATLAB 混合编程

C/FORTRAN 将双下溢设置为零

c_cpp 捕获浮点异常

为啥启用浮点异常后出现多个陷阱错误

*** 由于未捕获的异常“NSRangeException”而终止应用程序,原因:“*** -[__NSArray0 objectAtIndex:]: index 0 beyond bounds fo