C ++ настройка среды с плавающей запятой - PullRequest
2 голосов
/ 18 марта 2020

Я изо всех сил пытаюсь установить std :: fenv переносимым способом.

На основании этой страницы cppreference кажется, что fesetexceptflag(const std::fexcept_t*,int) должен помочь мне сделать трюк , С другой стороны, я обнаружил, что GNU предлагает также функцию feenableexcept(int). Насколько я понимаю, feenablexcept - это спецификация GNU c, и, хотя у меня весьма вероятность того, что я всегда буду иметь доступ к вещам 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? Насколько я понимаю, он захватывает все биты в первом аргументе, которые активны во втором аргументе, и помещает их в fenv (что, похоже, и происходит). Но тогда это кажется неэффективным. С другой стороны, версия 2 имеет fenv с маской 0 и все же успешно поднимает SIGFPE. Я очень смущен.

Я использую g cc 8.2.0 на linux машине (Red Hat), если это может помочь.

Ответы [ 2 ]

1 голос
/ 18 марта 2020

Я неправильно истолковываю документацию 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 с тем же флагом. Вы модифицируете fexcept_t на sesetexceptflag, затем с помощью feclearexcept и feraiseexcept, а затем сохраняете его с помощью fegetexceptflag.

Бросок SIGFPE сигнала является расширением C99 J.5.17p1 . Это может произойти в дополнение или вместо установки errno или флагов с плавающей точкой. Расширение активируется с помощью функции GNU feenableexcept.

1 голос
/ 18 марта 2020

Я неправильно истолковываю документацию 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

Не существует стандартного способа сделать плавающее Точечные исключения поднимают сигнал. Именно эту функцию предоставляет вам glib c. Вы можете поднять сигнал самостоятельно, хотя:

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