簡體   English   中英

C++設置浮點異常環境

[英]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/0FE_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然后使用feclearexceptferaiseexcept修改fexcept_t ,然后使用fegetexceptflag保存它。

拋出SIGFPE信號是擴展C99 J.5.17p1 它可能會在設置 errno 或浮點標志之外或代替設置 errno 或浮點標志發生。 該擴展是使用 gnu 函數feenableexcept激活的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM