Есть ли стандартный способ проверить наличие Infinite и NaN в Fortran 90/95?

#fortran #nan

#fortran #fortran95

Вопрос:

Я пытался найти совместимый со стандартами способ проверки бесконечности и значений NaN в Fortran 90/95, но это оказалось сложнее, чем я думал.

  • Я попытался вручную создать переменные Inf и NaN, используя двоичное представление, описанное в IEEE 754, но я не нашел такой функциональности.
  • Я знаю о встроенном ieee_arithmetic модуле в Fortran 2003 с ieee_is_nan() и ieee_is_finite() внутренними функциями. Однако он поддерживается не всеми компиляторами (особенно gfortran начиная с версии 4.9).

Определение infinity и NaN в начале, как pinf = 1. / 0 и nan = 0. / 0 , кажется мне хакерским, и ИМХО может вызвать некоторые проблемы со сборкой — например, если некоторые компиляторы проверяют это во время компиляции, нужно было бы предоставить специальный флаг.

Есть ли способ, который я могу реализовать в стандартном Fortran 90/95?

 function isinf(x)
! Returns .true. if x is infinity, .false. otherwise
...
end function isinf
  

и isnan() ?

Комментарии:

1. gnu fortran 4.10 исправляет это

2. GCC 5 и более поздние версии поддерживают IEEE_ARITHMETIC , но поддержка более старых версий по-прежнему является проблемой и будет оставаться в течение длительного времени.

Ответ №1:

Простой способ без использования ieee_arithmatic — сделать следующее.

Бесконечность: определите свою переменную infinity = HUGE(dbl_prec_var) (или, если она у вас есть, переменную с четырехкратной точностью). Затем вы можете просто проверить, равна ли ваша переменная infinity by if(my_var > infinity) .

NAN: Это еще проще. По определению, NAN не равен ничему, даже самому себе. Просто сравните переменную с самой собой: if(my_var /= my_var) .

Комментарии:

1. Странно, что никто больше не упомянул случай NaN. Я думаю, они беспокоились, что вы можете использовать fortran на процессоре, отличном от ieee? Помимо этого, проверка NaN очень надежна. Проверка infinity, с другой стороны, немного менее элегантна, поскольку зависит от типа данных.

2. @amaurea: Вы можете решить проблему с типом данных, используя INTERFACE и связывая несколько типов, которые вы могли бы использовать, в один и тот же MODULE PROCEDURE .

3. Оптимизатор действительно оптимизирует проверку, если запрашивается fast_math или аналогичный. Увы, это даже оптимизирует isnan внутреннюю часть gfortran. Ответственность программиста заключается в том, чтобы позаботиться об этом, когда он запрашивает небезопасную быструю математику и все еще хочет обнаружить NaN.

4. HUGE — это наибольшее значение, отличное от бесконечности; условие myvar > HUGE(myvar) должно быть истинным, только если myvar бесконечен, но называть HUGE(myvar) бесконечностью вводит в заблуждение.

5. использование @jvriesem iee_arithmetic — это то, как это должно выполняться (поэтому, если ваш компилятор его не поддерживает, найдите другой / лучший компилятор). Это совместимый со стандартами способ, который достаточно функционален: переменные «бесконечны», когда они больше значения, которое не имеет смысла для кода (и бесконечно малы, когда меньше определенного значения); проверка NaN может быть оптимизирована с помощью некоторых компиляторов.

Ответ №2:

У меня недостаточно репутации для комментариев, поэтому я «отвечу» на предложение Рика Томпсона по тестированию infinity.

 if (A-1 .eq. A) 
  

Это также будет верно, если A является очень большим числом с плавающей запятой и 1 ниже точности A.

Простой тест:

 subroutine test_inf_1(A)
    real, intent(in) :: A
    print*, "Test (A-1 == A)"
    if (A-1 .eq. A) then
        print*, "    INFINITY!!!"
    else
        print*, "    NOT infinite"
    endif
end subroutine

subroutine test_inf_2(A)
    real, intent(in) :: A
    print*, "Test (A > HUGE(A))"
    if (A > HUGE(A)) then
        print*, "    INFINITY!!!"
    else
        print*, "    NOT infinite"
    endif
end subroutine


program test
    real :: A,B

    A=10
    print*, "A = ",A
    call test_inf_1(A)
    call test_inf_2(A)
    print*, ""

    A=1e20
    print*, "A = ",A
    call test_inf_1(A)
    call test_inf_2(A)
    print*, ""

    B=0.0 ! B is necessary to trick gfortran into compiling this
    A=1/B
    print*, "A = ",A
    call test_inf_1(A)
    call test_inf_2(A)
    print*, ""

end program test
  

выходы:

 A =    10.0000000    
Test (A-1 == A)
    NOT infinite
Test (A > HUGE(A))
    NOT infinite

A =    1.00000002E 20
Test (A-1 == A)
    INFINITY!!!
Test (A > HUGE(A))
    NOT infinite

A =          Infinity
Test (A-1 == A)
    INFINITY!!!
Test (A > HUGE(A))
    INFINITY!!!
  

Ответ №3:

Нет.

Основные части IEEE_ARITHMETIC для генерации / проверки NaN достаточно легко написать для gfortran для конкретной архитектуры.

Комментарии:

1. Что касается вашего второго утверждения, они, похоже, не согласны gcc.gnu.org/ml/gcc-bugs/2012-10/msg00580.html или я что-то упускаю?

2. Кстати, я добавил ссылку на соответствующую ошибку gfortran (которая датируется 2006 годом и имеет статус NEW).

3. Написать все IEEE_ARITHMETIC для всех архитектур, которые поддерживает gfortran, с чем связана эта ошибка, было бы сложно! Записать биты выбора, которые генерируют / проверяют наличие NaN, для конкретной архитектуры (например, x64) довольно просто. Смотрите sites.google.com/site/tprincesite/Home/gfortran-ieee-arithmetic в одном примере Тима Принса, который основан на неравенстве любых двух NaN, альтернативный подход заключается в использовании встроенных битов Fortran для генерации / тестирования конкретных шаблонов, которые указывают на NaN. (Смотрите «Оригинальную» ошибку gfortran 29383 для более подробного обсуждения.)

Ответ №4:

Я использовал:

   PROGRAM MYTEST
  USE, INTRINSIC :: IEEE_ARITHMETIC, ONLY: IEEE_IS_FINITE      
  DOUBLE PRECISION :: number, test
  number = 'the expression to test'
  test = number/number
  IF (IEEE_IS_FINITE(test)) THEN
     WRITE(*,*) 'We are OK'
  ELSE
     WRITE(*,*) 'Got a problem'
  END IF         
     WRITE(*,*) number, test
  END PROGRAM MYTEST
  

При этом будет выведено ‘Возникла проблема’ для number = 0.0D0, 1.0D0 /0.0D0, 0.0D0 /0.0D0, SQRT(-2.0D0), а также для переполнений и недопотоков, таких как number = EXP(1.0D800) или number = EXP(-1.0D800). Обратите внимание, что обычно такие вещи, как number = EXP (1.0D-800), просто устанавливают number = 1.0 и выдают предупреждение во время компиляции, но программа напечатает «У нас все в порядке», что я нахожу приемлемым.

OL.

Комментарии:

1. В вопросе задается не число, а ваш пример проверяет конечность. Он также поймает Inf и -Inf как ложные срабатывания. Также обратите внимание, что вопрос действительно в основном интересовал, что делать, когда IEEE_ARITHMETIC недоступно, и спрашивает, как это сделать, используя Fortran 90/95.

2. В дополнение к опасениям Владимира Ф., нет гарантии, что даже при ieee_arithmetic этом ieee_support_datatype(test) это правда. Если это не так, то рассматривать это запрещено ieee_is_finite(test) .

3. Я был слишком резок, вопрос также требует эквивалента IEEE_IS_FINITE() . Но дело в том, что OP знает, что он существует, но запрашивает альтернативу.

Ответ №5:

для тестирования NaN ничего не сработало, например. если тестировать реальный s2p, чтобы увидеть, является ли это NaN, тогда

 if(isnan(s2p)) 
  

не работает в gfortran и не

 if(s2p.ne.s2p). 
  

Единственное, что сработало, это

 if(.not.s2p<1.and..not.s2p>1)  
  

хотя, чтобы убедиться, что вы можете добавить

 if(.not.s2p<1.and..not.s2p>1.and..not.s2p==1)    
  

Ответ №6:

Нет.

В Fortran 90/95 также нет способа проверки бесконечности или NAN, соответствующего стандартам, и не может быть способа, соответствующего стандартам. В Fortran 90/95 нет совместимого со стандартами способа определения любого из этих квазичислов.

Комментарии:

1. Ну, в рамках стандарта IEEE с плавающей запятой, то есть.

Ответ №7:

Для Fortran 1 / infinity = 0 таким образом, разделите вашу переменную на ноль, т. Е.

 program test
implicit none
real :: res
integer :: i

do i=1,1000000
    res=-log((1. (10**(-real(i))))-1.)
    print*,i,res
    if ((1./res)==0.) then
        exit
    end if
end do

end program
  

вот ваша проверка на бесконечность. Никаких усложнений не требуется.

Ответ №8:

Для Inf, похоже, работает, что если (A-1 .eq. A) верно, тогда A является Inf

Комментарии:

1. Возможно, вы захотите уточнить немного больше.