C++设置浮点异常环境
C++ setting floating point exception environment
我正在努力尝试以便携方式设置 std::fenv。
根据 this cppreference 页面,似乎 fesetexceptflag(const std::fexcept_t*,int)
应该可以帮我解决这个问题。另一方面,我发现 GNU 也提供了 feenableexcept(int)
功能。我的理解是 feenablexcept
是特定于 GNU 的,虽然我 非常 很可能总是可以访问 GNU 的东西,但我希望只使用 std 的东西,也就是说, 坚持 fesetexceptflag
。
我写了一个小测试,发现 feenableexcept
方法有效,而 fesetexceptflag
方法无效。这是两个例子。将main开头两行的注释互换,得到版本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 的掩码 fenv 为 0,但仍成功提高了 SIGFPE。我很困惑。
我在 linux 机器 (Red Hat) 上使用 gcc 8.2.0,如果有帮助的话。
Am I misinterpreting the documentation of 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);
Am I misinterpreting the documentation of fesetexceptflag?
是的。例如,当您执行 1.0/0
时,FE_DIVBYZERO
标志 gets 在当前环境中出现。 fesetexceptflag
不允许您决定当您除以 0 时 将 发生什么。fesetexceptflag
让我们检查 if您已经做过 的操作导致了异常。
您只能看到使用 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
的浮点异常。你用 sesetexceptflag
修改 fexcept_t
然后用 feclearexcept
和 feraiseexcept
然后用 fegetexceptflag
.
保存它
发出 SIGFPE
信号是一个扩展 C99 J.5.17p1。它可以作为设置 errno 或浮点标志的补充或替代发生。使用 gnu 函数 feenableexcept
.
激活扩展
我正在努力尝试以便携方式设置 std::fenv。
根据 this cppreference 页面,似乎 fesetexceptflag(const std::fexcept_t*,int)
应该可以帮我解决这个问题。另一方面,我发现 GNU 也提供了 feenableexcept(int)
功能。我的理解是 feenablexcept
是特定于 GNU 的,虽然我 非常 很可能总是可以访问 GNU 的东西,但我希望只使用 std 的东西,也就是说, 坚持 fesetexceptflag
。
我写了一个小测试,发现 feenableexcept
方法有效,而 fesetexceptflag
方法无效。这是两个例子。将main开头两行的注释互换,得到版本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 的掩码 fenv 为 0,但仍成功提高了 SIGFPE。我很困惑。
我在 linux 机器 (Red Hat) 上使用 gcc 8.2.0,如果有帮助的话。
Am I misinterpreting the documentation of 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);
Am I misinterpreting the documentation of fesetexceptflag?
是的。例如,当您执行 1.0/0
时,FE_DIVBYZERO
标志 gets 在当前环境中出现。 fesetexceptflag
不允许您决定当您除以 0 时 将 发生什么。fesetexceptflag
让我们检查 if您已经做过 的操作导致了异常。
您只能看到使用 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
的浮点异常。你用 sesetexceptflag
修改 fexcept_t
然后用 feclearexcept
和 feraiseexcept
然后用 fegetexceptflag
.
发出 SIGFPE
信号是一个扩展 C99 J.5.17p1。它可以作为设置 errno 或浮点标志的补充或替代发生。使用 gnu 函数 feenableexcept
.