Сброс в ноль в gfortran

Есть ли способ принудительно обнулить потери в gfortran?

Я не могу поверить, что это первый раз, когда кто-то спрашивает об этом, но я нигде не мог найти ничего об этом. Моя вина, если это дубликат.


person bob.sacamento    schedule 29.09.2015    source источник
comment
Пожалуйста, используйте общий fortran, не многие люди следят за отдельными версиями, и ваш вопрос вообще не зависит от версии Fortran.   -  person Vladimir F    schedule 29.09.2015
comment
некоторое обсуждение этой темы здесь: gcc.gnu.org/bugzilla/show_bug.cgi ?id=36821   -  person agentp    schedule 29.09.2015


Ответы (2)


Этого можно добиться с помощью последних версий gfortran, которые поддерживают модули Fortran 2003 IEEE. Стандарт определяет два режима потери значимости — постепенный и резкий. Внезапный - это тот, который вам нужен, который устанавливает значение потери значимости в 0 и сигнализирует об исключении с плавающей запятой. Вы можете протестировать поддержку управления режимом потери значимости с помощью функции ieee_support_underflow_control(X), которая проверяет контроль потери значимости для типа реального X и возвращает логическое значение true, если оно поддерживается. Если поддерживается, вы можете call ieee_set_underflow_mode(.false.) установить резкий режим потери значимости.

Ниже приведена тестовая программа, которую вы можете использовать для проверки поддержки управления недостатком памяти для реального вида по умолчанию:

program test
  use, intrinsic :: ieee_arithmetic
  use, intrinsic :: iso_fortran_env, only: compiler_version, compiler_options
  implicit none
  logical :: underflow_support, gradual, underflow
  real :: fptest
  integer :: i

  print '(4a)',  'This file was compiled by ', &
       compiler_version(), ' using the options ', &
       compiler_options()
  fptest = 0.0
  underflow_support = ieee_support_underflow_control(fptest)
  if (underflow_support) then
     print *,'Underflow control supported for the default real kind'
  else
     stop 'no underflow control support'
  end if

  call ieee_set_underflow_mode(.false.)
  call ieee_get_underflow_mode(gradual)
  if (.not.gradual) then 
     print *,'Able to set abrupt underflow mode'
  else
     stop 'error setting underflow mode'
  end if

  fptest = 2e-36
  do i=1,50 ! 50 iterations max
     fptest = fptest * 0.5
     print '(e15.10)',fptest
     call ieee_get_flag(ieee_underflow,underflow)
     if (underflow) print *,'Underflow exception signaling'
     if (fptest == 0.0) exit
  end do

end program test

Используя gfortran версии 5.2.0, эта программа выводит:

This file was compiled by GCC version 5.2.0 using the options -mtune=generic -march=x86-64 -fno-unsafe-math-optimizations -frounding-math -fsignaling-nans
 Underflow control supported for the default real kind
 Able to set abrubpt underflow mode
.1000000036E-35
.5000000180E-36
.2500000090E-36
.1250000045E-36
.6250000225E-37
.3125000112E-37
.1562500056E-37
.0000000000E+00
 Underflow exception signaling

Флаги опций компилятора -fno-unsafe-math-optimizations -frounding-math -fsignaling-nans предлагаются документацией gfortran 5.2 для использования каждый раз, когда модули IEEE используются для обеспечения соответствия стандарту.

person casey    schedule 30.09.2015
comment
Очень полный ответ. Благодарю вас! - person bob.sacamento; 30.09.2015

Ленивый способ «сбросить в ноль» — использовать -funsafe-math-optimizations gfortran для:

Разрешить математическую оптимизацию, которая может нарушать стандарты IEEE или ISO.

или другими словами:

Этот режим включает оптимизации, которые допускают произвольные повторные ассоциации и преобразования без гарантий точности. Он также не пытается сохранить знак нулей.

Например, small.f:

      program test
        real r
        r=1e-40
        print *,'r on next line'
        print *,r
      end program

Без всяких флагов показывается ненулевое денормальное (маленькое) число, без ошибки:

$ gfortran -g small.f
$ ./a.out
 r on next line
   9.99994610E-41

Перехватите денормализованное число, которое вылетает при попытке напечатать значение:

$ gfortran -g -ffpe-trap=denorm small.f
$ ./a.out
 r on next line

Program received signal SIGFPE: Floating-point exception - erroneous arithmetic operation.

Backtrace for this error:
#0  0x2aaaab05c26f in ???
#1  0x2aaaaac61aed in get_float_string
        at ../../../libgfortran/io/write_float.def:1064
#2  0x2aaaaac6423d in list_formatted_write_scalar
        at ../../../libgfortran/io/write.c:1889
#3  0x4008f1 in test
        at /path/to/small.f:5
#4  0x400941 in main
        at /path/to/small.f:6
Floating point exception

И добавил флаг, который сбрасывает его в ноль:

$ gfortran -g -ffpe-trap=denorm -funsafe-math-optimizations small.f
$ ./a.out
 r on next line
   0.00000000
person Mike T    schedule 12.03.2019