[英]C++ setting floating point exception environment
我正在努力嘗試以可移植的方式設置 std::fenv 。
基於這個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 有一個 0 掩碼 fenv,但成功地提高了 SIGFPE。 我很困惑。
我在 linux 機器(Red Hat)上使用 gcc 8.2.0,如果有幫助的話。
我是否誤解了 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);
我是否誤解了 fesetexceptflag 的文檔?
是的。 例如,當您執行1.0/0
, FE_DIVBYZERO
標志會在當前環境中升高。 fesetexceptflag
不會讓您決定何時除以0會發生什么fesetexceptflag
讓你檢查,如果你已經沒有導致異常的操作的。
您只能使用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
然后使用feclearexcept
和feraiseexcept
修改fexcept_t
,然后使用fegetexceptflag
保存它。
拋出SIGFPE
信號是擴展C99 J.5.17p1 。 它可能會在設置 errno 或浮點標志之外或代替設置 errno 或浮點標志發生。 該擴展是使用 gnu 函數feenableexcept
激活的。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.