How can I call from R a Fortran subroutine with a character argument? My attempt to do so does not work, although I am able to call Fortran subroutines with double precision arguments. For the Fortran code
subroutine square(x,x2)
double precision, intent(in) :: x
double precision, intent(out) :: x2
x2 = x*x
end subroutine square
subroutine pow(c,x,y)
character (len=255), intent(in) :: c
double precision, intent(in) :: x
double precision, intent(out) :: y
if (c == "s") then
y = x**2
else if (c == "c") then
y = x**3
else
y = -999.0d0 ! signals bad argument
end if
end subroutine pow
and R code
dyn.load("power.dll") # dll created with gfortran -shared -fPIC -o power.dll power.f90
x <- 3.0
foo <- .C("square_",as.double(x),as.double(0.0))
print(foo)
bar <- .C("pow_",as.character("c"),as.double(x),as.double(0.0))
print(bar)
The output from
C:\programs\R\R-3.6.1\bin\x64\rterm.exe --vanilla --slave < xcall_power.r
is
[[1]]
[1] 3
[[2]]
[1] 9
[[1]]
[1] "c"
[[2]]
[1] 3
[[3]]
[1] -999
When you use .C to call a Fortran subroutine the calling treats character arguments as being C-style char **. This is not compatible with the Fortran dummy argument of type character(len=255).
You have two 'simple' approaches available:
modify the Fortran subroutine to accept arguments looking like char **
use .Fortran instead of .C
Modifying the Fortran subroutine to use C interoperability with char ** is better the subject of a new question (for its breadth and being not specific to your R problem). In general, I prefer writing Fortran procedures to be used in R as exposing a C interoperable interface and .C or .Call. With what follows you may also come to that conclusion.
Even the R documentation is not optimistic about passing character arguments with .Fortran:
‘.Fortran’ passes the first (only) character string of a character vector as a C character array to Fortran: that may be usable as ‘character*255’ if its true length is passed separately. Only up to 255 characters of the string are passed back. (How well this works, and even if it works at all, depends on the C and Fortran compilers and the platform.)
You will need to read your documentation about argument passing conventions, such as that for gfortran (consult the appropriate version as these conventions may change).
With .Fortran and gfortran then with a procedure that is not C-interoperable you will need to pass a "hidden" argument to the Fortran procedure specifying the length of the character argument. That is true for explicit length characters (constant length, even length-1, or not) and assumed-length characters.
For gfortran before version 7, this hidden argument is of (R) type integer. Using pow of the question, or with assumed-length argument, we can try something like
bar <- .Fortran("pow", as.character("c"), as.double(x), as.double(0.0), 255L)
Note, however, that this is not standard and inherently not portable. Indeed, as janneb comments and the documentation linked above state, how you pass this hidden argument from R to a gfortran-compiled procedure depends on the version of gfortran used. Using 255L at the end probably won't work beyond gfortran 7. Instead you will need to pass the hidden argument as something matching an integer(c_size_t) (possibly a 64-bit integer). For compilers other than gfortran you may need to do something quite different.
It really is best to use a C-interoperable procedure either with argument interoperable with char ** (using .C) or char [] (using .Fortran). As I say, it's worth going for the first option here, as that leaves more flexibility (like longer characters, more portability, and more character arguments).
Related
I'd like to turn a legacy Fortran code into modern Fortran compliant code, so I can turn on compiler warnings, interface checking, etc. At this stage I don't want to change the functionality, just make it work as close as possible to what it was, and still keep compilers happy.
My current problem is that the code at many places passes arrays of the wrong types, e.g. a real array to a subroutine that has an integer dummy argument. This is not a bug per se in the code, since it is intentional and it works as intended (at least in common configurations). Now, how could I do the same and while keeping the code compliant? Consider the following example:
program cast
implicit none
double precision :: a(10)
call fill_dble(a,10)
call print_dble(a,10)
call fill_int(a,10)
!call fill_int(cast_to_int(a),10)
call print_dble(a,10)
call print_int(a(1),10)
!call print_int(cast_to_int(a),10)
call print_dble(a(6),5)
contains
function cast_to_int(a) result(b)
use iso_c_binding
implicit none
double precision, target :: a(*)
integer, pointer :: b(:)
call c_f_pointer(c_loc(a(1)), b, [1])
end function
end program
subroutine fill_dble(b,n)
implicit none
integer :: n, i
double precision :: b(n)
do i = 1, n
b(i) = i
end do
end subroutine
subroutine print_dble(b,n)
implicit none
integer :: n
double precision :: b(n)
write(6,'(10es12.4)') b
end subroutine
subroutine fill_int(b,n)
implicit none
integer :: n, b(n), i
do i = 1, n
b(i) = i
end do
end subroutine
subroutine print_int(b,n)
implicit none
integer :: n, b(n)
write(6,'(10i4)') b
end subroutine
When I compile it and run it (gfortran 4.8 or ifort 18), I get, as expected:
1.0000E+00 2.0000E+00 3.0000E+00 4.0000E+00 5.0000E+00 6.0000E+00 7.0000E+00 8.0000E+00 9.0000E+00 1.0000E+01
4.2440-314 8.4880-314 1.2732-313 1.6976-313 2.1220-313 6.0000E+00 7.0000E+00 8.0000E+00 9.0000E+00 1.0000E+01
1 2 3 4 5 6 7 8 9 10
6.0000E+00 7.0000E+00 8.0000E+00 9.0000E+00 1.0000E+01
The first half of the real array is corrupted with integers (because integers are half the size), but when printed as integers the "right" values are there. But this is non-compliant code. When I try to fix it by activating the cast_to_int function (and disabling the calls without it) I get indeed something that compiles without warning, and with gfortran I get the same result. With ifort, however, I get:
1.0000E+00 2.0000E+00 3.0000E+00 4.0000E+00 5.0000E+00 6.0000E+00 7.0000E+00 8.0000E+00 9.0000E+00 1.0000E+01
1.0000E+00 2.0000E+00 3.0000E+00 4.0000E+00 5.0000E+00 6.0000E+00 7.0000E+00 8.0000E+00 9.0000E+00 1.0000E+01
0******** 0 5 6 7 8 9 10
6.0000E+00 7.0000E+00 8.0000E+00 9.0000E+00 1.0000E+01
which I can't understand. Moreover, ifort with -O0 crashes (and it doesn't with the other version).
I know the code is still not quite correct, because the pointer returned by cast_to_int is still of size 1, but I believe that should be a different problem.
What am I doing wrong, or how can I get ifort do what I want?
EDIT: Following #VladimirF's reply, I add, after implicit none:
subroutine fill_int(b,n)
!dec$ attributes no_arg_check :: b
integer :: n, b(n)
end subroutine
subroutine print_int(b,n)
!dec$ attributes no_arg_check :: b
integer :: n, b(n)
end subroutine
end interface
but compiling with warnings on still gives me an error:
$ ifort cast2.f90 -warn all
cast2.f90(17): error #6633: The type of the actual argument differs from the type of the dummy argument. [A]
call fill_int(a,10)
--------------^
cast2.f90(20): error #6633: The type of the actual argument differs from the type of the dummy argument. [A]
call print_int(a(1),10)
---------------^
compilation aborted for cast2.f90 (code 1)
Intel Fortran supports the !dec$ attributes no_arg_check directive. It instructs the compiler "that type and shape matching rules related to explicit interfaces are to be ignored".
"It can be applied to an individual dummy argument name or to the routine name, in which case the option is applied to all dummy arguments in that interface."
It should be applied to a module procedure (or an interface block), so you should move your functions and subroutines into a module.
Many other compilers have similar directives.
What is wrong about your code? As a rule of thumb, do not ever use any Fortran functions that return pointers. They are pure evil. Fortran pointers are completely different from C pointers.
When you do call fill_int(cast_to_int(a),10) what happens is that the expression cast_to_int(a) is evaluated and the result is an array. Now depending on the optimizations the compiler may choose to pass the address of the original pointer, but it may also create a copy of the result integer array and pass a copy to the subroutine.
Also, your array a does not have the target attribute, so the address used inside cast_to_int(a) is only valid inside the function and is not valid after it returns.
You should make the b inside the main program and just pass b instead of a. It will work similar to equivalence. Looking at the values stored as a different type will be not standard-conforming anyway. This form of type punning is not allowed.
I found a possible general solution that seems to work. The code I have to deal with looks something like this:
subroutine some_subroutine(a,b,c,d,...)
real a(*),b(*),c(*),d(*)
! many more declarations, including common blocks
!...
call other_subroutine(a,b(idx),c,...)
!...
end subroutine some_subroutine
! this typically in another file:
subroutine other_subroutine(x,y,z,...)
real x(*)
integer y(*)
logical z(*)
! other declarations and common blocks
! unreadable code with calls to other procedures
! not clear which which arguments are input and output
end subroutine other_subroutine
I now modify it to be:
subroutine some_subroutine(a,b,c,d,...)
real a(*),b(*),c(*),d(*)
! many more declarations, including common blocks
call inner_sub(b,c)
contains
subroutine inner_sub(b,c)
use iso_c_binding
real, target :: b(*),c(*)
integer, pointer :: ib(:)
logical, pointer :: lc(:)
!...
call c_f_pointer(c_loc(b(idx)),ib,[1]) ! or use the actual length if I can figure it out
call c_f_pointer(c_loc(c(1)),lc,[1])
call other_subroutine(a,ib,lc,...)
nullify(ib,lc)
!...
end subroutine inner_sub
end subroutine some_subroutine
leaving other_subroutine untouched. If I use directly the target attribute on the outer routine, I have to add an explicit interface to anything calling it, so instead I wrap the inner code. By using contains I don't need to pass all variables, just those that will be "punned". The c_f_pointer call should be done right before the problematic call, since index variables (idx in the example) could be in common blocks and changed in other calls, for example.
Any pitfalls, apart from those already present in the original code?
Well, I recently fell in love with Fortran (f90) and have been trying to understand the "kung-fu" of R and Fortran. I found several relevant and helpful questions here (e.g. this and this).
What I am trying to do:
I am (probably, trying to do something crazy) trying to call the following .f90 subroutines in R (x64) using .Fortran() function. Here is the test.f90 code:
! Computes the square of a number
Subroutine sr1(a,b)
!DEC$ ATTRIBUTES DLLEXPORT::sr1
!DEC$ ATTRIBUTES C, REFERENCE, ALIAS:'sr1' :: sr1
implicit none
integer a,b
b = a*a
End Subroutine sr1
! Computes the cube of a number
Subroutine sr2(x,y)
!DEC$ ATTRIBUTES DLLEXPORT::sr2
!DEC$ ATTRIBUTES C, REFERENCE, ALIAS:'sr2' :: sr2
implicit none
integer x,y
y = x*x*x
End Subroutine sr2
I am compiling the above test.f90 code via gfortran on my Windows 10 machine by:
gfortran -shared -o test.dll test.f90
The compilation works and I get the test.dll. Now, in R. I try to load it:
dyn.load("path_to_file/test.dll")
It works. But, it fails here:
> is.loaded("test")
[1] False
I already found a relevant question here. But, I could not get the clue to fix my problem. Can someone suggest some workaround to fix the issue?
I am not a big user of R, but my tests show that while
is.loaded("test_R")
indeed returns FALSE, both
is.loaded("sr1")
and
is.loaded("sr2")
return TRUE. But I did my tests on Linux and GCC which may interpret the extrnally visible subroutine names differently.
While implementing a string utility function, I came across a couple of character pointer expressions that I think may be unsafe. I googled, searched on SO, read my Fortran 95 language guide (Gehrke 1996) as well as various excerpts on display in Google books. However, I could not find any sources discussing this particular usage.
Both ifort and gfortran compile the following program without warning:
PROGRAM test_pointer
IMPLICIT NONE
CHARACTER(LEN=100), TARGET :: string = "A string variable"
CHARACTER(LEN=0), TARGET :: empty = ""
CHARACTER(LEN=:), POINTER :: ptr
ptr => NULL()
IF(ptr == "") PRINT *, 'Nullified pointer is equal to ""'
ptr => string(-2:-3)
IF(ptr == "") PRINT *, 'ptr equals "", but the (empty) sub string was out of bounds.'
ptr => empty(1:0)
IF(ptr == "") PRINT *, 'ptr equals "", it was not possible to specify subarray within bonds'
END PROGRAM
The output of the program is:
Nullified pointer is equal to ""
ptr equals "", but the (empty) sub string was out of bounds.
ptr equals "", it was not possible to specify subarray within bonds
So apparently, the evaluations of the pointer make sense to the compiler and the outcome is what you would expect. Can somebody explain why the above code did not result in at least one segmentation fault? Does the standard really allow out-of-bounds substrings? What about the use of a nullified character pointer?
edit : After reading Vladimir F's answer, I realized that I forgot to activate runtime checking. The nullified pointer actually does trigger a run time error.
Why they do not result in a segfault? Dereferencing a nullified pointer is not conforming to the standard (in C terms it is undefined behaviour). The standard does not say what a non-conforming program should do. The standard only applies to programs which conform to it! Anything can happen for non-conforming programs!
I get this (sunf90):
****** FORTRAN RUN-TIME SYSTEM ******
Attempting to use an unassociated POINTER 'PTR'
Location: line 8 column 6 of 'charptr.f90'
Aborted
and with another compiler (ifort):
forrtl: severe (408): fort: (7): Attempt to use pointer PTR when it is not associated with a target
Image PC Routine Line Source
a.out 0000000000402EB8 Unknown Unknown Unknown
a.out 0000000000402DE6 Unknown Unknown Unknown
libc.so.6 00007FA0AE123A15 Unknown Unknown Unknown
a.out 0000000000402CD9 Unknown Unknown Unknown
For the other two accesses, you are not accessing anything, you are creating a substring of length 0, there is no need to access the character variable, the result is just an empty string.
Specifically, the Fortran standard (F2008:6.4.1.3) says this about creating a substring:
Both the starting point and the ending point shall be within the
range 1, 2, ..., n unless the starting point exceeds the ending
point, in which case the substring has length zero.
For this reason the first part is not standard conforming, but the other ones are.
I am working with R, but need to do a lot of number crunching, which I want to do in fortran. I am relatively new to R and a newbie to fortran... I already have a working R program, which I would like to optimize. I created a Fortran program solving a system of ODE's, which I keep in a subroutine. I additionally use a module called aux.f90 to store the parameters and a function which creates a signal that is fed into the equations. This works as intended and the data is saved in a .txt file.
What I would like to do now is to create an R front-end that tosses the Fortran program the parameters, such as the length of the simulation or the number of steps used in the solution. Then Fortran does the heavy lifting, saves the results in the file and I can use R to visualize the data from the file. See the Fortran code below:
! The auxiliary module contains all parameters
module aux
implicit none
integer,parameter :: n = 2**8 ! number of steps
real(kind=4) :: jout = 0.5 ! for normal metabolism
real(kind=4) :: alp = 4.0 ! factor for growth
real(kind=4) :: bet = 1.0 ! benefit value
real(kind=4) :: etay = 0.1 ! cost value y
real(kind=4) :: etaz = 0.10 ! cost value z
real(kind=4) :: h = 3.0 ! Hill coefficient
real(kind=4) :: Kx = 0.07 ! saturation coefficient
real(kind=4) :: t1 = 0.0, t2 = 30.0 ! start and end point of the simulation
contains ! function f(t) to create a step signal
real(kind=4) function f(t)
implicit none
real(kind=4), intent(in) :: t ! taking time value
real(kind=4) :: tt
!real(kind=4), intent(out) :: s ! giving out the signal
real(kind=4) :: period = 5 ! defining the period length
tt = MODULO(t,period)
! Signal function
if (tt > 0.5*period) then
f = 1
else
f = 0
endif
end function
end module aux
! The program solving the ODE system and giving the output as a fileprogram ffl
program ffl
use aux ! Use module aux
implicit none
integer :: m,j ! iteration variable
real(kind=4), dimension(6) :: b =(/0.0, 0.2, 0.4, 0.6, 0.8, 1.0/) ! expression
real(kind=4) :: dt ! time resolution
real(kind=4), dimension(n) :: t ! time vector
real(kind=4), dimension(4) :: x_new, x_aux, y_new, y_aux, q0 ! vectors
! computing the time vector
dt=(t2-t1)/real(n) ! calculating time resolution
t(1) = t1 ! setting first time value to t1 = 0
do m = 1,n ! filling the time vector
t(m) = t(m-1)+dt
end do
open(unit = 10,file = 'ffl.txt', status = 'unknown')
do j=1,6
k = b(j)
! initial conditions
q0(1) = 0 ! x
q0(2) = k ! y
q0(3) = 0 ! z
q0(4) = 0 ! w
!open(unit = 10,file = 'ffl.txt', status = 'unknown')
x_new = q0 ! set initial conditions
write(10,*)t(1),x_new(1),x_new(2),x_new(3),x_new(4) ! saving data
do m = 2,n
! Solving with a second order method
call derivate(t(m-1),x_new,y_new) ! call the derivate routine
x_aux = x_new + dt*y_new
call derivate(t(m),x_aux,y_aux)
x_new = x_new + 0.5*dt*(y_new+y_aux)
write(10,*)t(m),x_new(1),x_new(2),x_new(3),x_new(4) ! saving data
end do
end do
close(10)
end program ffl
! The subroutine derivate gives the system of ODE's to be solved
subroutine derivate(time,y,z)
use aux ! Use module aux
implicit none
real(kind=4), intent(in) :: time ! input: time vector
real(kind=4), dimension(4), intent(in) :: y ! input: initial conditions vector
real(kind=4), dimension(4), intent(out):: z ! output: results vector
z(1) = f(time)-y(1) !dx/dt
z(2) = k+(1-k)*((1+Kx)*(y(1)**h))/((y(1)**h)+Kx)-y(2) !dy/dt
z(3) = ((1+Kx)*(y(1)**h)/((y(1)**h)+Kx)*((1+Kx)*(y(2)**h))/((y(2)**h)+Kx)-y(3)) !dz/dt
z(4) = f(time)*y(3)-etay*(k+(1-k)*((1+Kx)*(y(1)**h))/((y(1)**h)+Kx)) & !dw/dt
-etaz*(((1+Kx)*(y(1)**h))/((y(1)**h)+Kx)*((1+Kx)*(y(2)**h))/((y(2)**h)+Kx))
end subroutine derivate
I have read the "Writing R extensions" document, but did not find it very helpful...
NOW to the questions: Since R needs a Fortran subroutine, i would like to create a wrapper subroutine in fortran that makes use of my existing files, which I can then call from R. However, i can't find a way to create this wrapper subroutine in the first place. Is it even possible to call an actual program in a subroutine? I couldn't find anything helpful online.
A program is supposed to be linked as an executable, so you can't call it from a subroutine - or you call the executable (with SYSTEM in gfortran), but you could do that directly from R.
The easy way to call Fortran from R is the .Fortran R function, which calls a Fortran subroutine (not a function, nor a program).
The basic steps are :
compile a Fortran DLL, exporting the subroutines you need (of course they may be wrappers for other subroutines or functions)
put the DLL in a directory in your system path
from R, load the DLL with dyn.load
call your subroutine with .Fortran.
If you use gfortran, you may just install Rtools, which has everything you need. If you want to use another compiler, you may have some trouble, especially with names.
From your comment to user2188538's answer, I see you already know all these steps, but be very careful with symbol names. From the .Fortran help: Use .Fortran with care for compiled Fortran 9x code: it may not work if the Fortran 9x compiler used differs from the Fortran 77 compiler used when configuring R, especially if the subroutine name is not lower-case or includes an underscore. It is also possible to use .C and do any necessary symbol-name translation yourself.
Also, I suspect your wrapper subroutine should not reside inside a module, or you may have extra trouble with names. But this is only a limitation for the wrapper function, which must be visible from R.
You can check the exported names in your DLL (send objdump -x your.so to a file and look for exported symbols). And check also in R, with is.loaded("your.symbol"), after loading the DLL. Be aware that usually, gfortran appends an extra underscore to names, whereas it's not needed when you call .Fortran from R. As described above, you may use .C instead (but then, remember Fortran arguments are passed by reference).
To check that you understand the whole process, I suggest you test it on a trivial example, such as a unique subroutine mysub(x,y,z) that does only z=x+y. When this one runs, you can elaborate on it to call more complex routines.
edit
You should not use assumed-shape or deferred-shape arrays, when you pass arrays arguments from R to Fortran, but only assumed-size arrays, that is, the usual array passing in Fortran 77. This is because R knows only how to pass a pointer to raw data, whereas assumed-shape and deferred-shape need more information, and R does not know the data structure to do that.
For example, you can do that:
subroutine mysub(n, a)
real :: a(n, n)
...
end subroutine
But this will amost certainly fail:
subroutine mysub(a)
real :: a(:, :)
...
end subroutine
Also, you can't pass function arguments from R to Fortran, as that would need a special data structure for the callback (under the hood, R is a Scheme dialect, and it uses S-expressions). You may do that in C, through .C or .Call (see help for .Call and R Internals).
You can indeed call Fortran from R using the .Foreign function in the R base package (see ?.Foreign). For some clear examples on how to do this, see the following page on how to call Fortran (as well as C) from R: http://users.stat.umn.edu/~geyer/rc/
to get +-inf on 64 bit system i used the next code
double precision, parameter :: pinf = transfer(z'7FF0000000000000',1d0) ! 64 bit
double precision, parameter :: ninf = transfer(z'FFF0000000000000',1d0) ! 64 bit
and it works well.
On 32-bit
I've got an compilation error only(!) for ninf:
double precision, parameter :: ninf = transfer(z'FFF0000000000000',1d0
1
Error: Integer too big for integer kind 8 at (1)
assignment ninf = -pinf not helps and leads to compilation Arithmetic overflow error:
double precision, parameter :: ninf = -pinf
1
Error: Arithmetic overflow at (1)
I know about ieee_arithmetic module but gcc don't handle it.
Is there any multi-architecture way to set constants to positive/negative infinities?
Update
Gfortran option -fno-range-check suppress errors and successfully compile that code.
It's not important but I'm still interesting.
Why gfortran allows constant definition of +Infinity but yelling in loud about exactly the same thing with -Infinity?
In this case gfortran is internally representing your hexadecimal ("Z") literals as the largest unsigned integer size available. Since transfer is a Fortran intrinsic, and Fortran does not have unsigned integers, the first thing gfortran does is to assign the literal to a signed type, which causes your bit pattern for negative infinity to overflow. This happens in many other cases where you use BOZ literals, and I think that this is a bug in gfortran.
I think this only shows up on a 32 bit system because on your 64 bit system, gfortran probably has a 128 bit integer type available; a 128 bit signed integer will not "overflow" with that bit pattern.
But it is also the case that your code does not conform to the Fortran standard, which says that hex literals can only appear inside data statements or the functions int, real, or dble. However, putting a hex literal in dble does the same thing as transfer anyway. If gfortran did not have a bug in it, your program would work, but it would technically be incorrect.
Anyway, the following code works for me in gfortran, and I believe it will solve your problem in a way that's standard-compliant and avoids -fno-range-check:
integer, parameter :: i8 = selected_int_kind(13)
integer, parameter :: r8 = selected_real_kind(12)
integer(i8), parameter :: foo = int(Z'7FF0000000000000',i8)
integer(i8), parameter :: bar = ibset(foo,bit_size(foo)-1)
real(r8), parameter :: posinf = transfer(foo,1._r8)
real(r8), parameter :: neginf = transfer(bar,1._r8)
print *, foo, bar
print *, posinf, neginf
end
Output:
9218868437227405312 -4503599627370496
Infinity -Infinity
The key is to create the pattern for positive infinity first (since it works), and then create the pattern for negative infinity by simply setting the sign bit (the last one). The ibset intrinsic is only for integers, so you then have to use transfer on those integers to set your real positive/negative infinity.
(My use of i8/r8 is just habit, since I've worked with compilers where the kind parameter was not equal to the number of bytes. They are both equal to 8 in this case.)
I'm not using the same compiler as you are (I'm using g95 with compiler option -i4 set for 32-bit integers, and one workaround (if you're staunch about using transfer for that purpose) that I found was to specify the integer argument as a parameter like so:
Note: with my compiler, I was able to assign the number directly to the parameter. I'm not sure if it's the same on yours, but I'm pretty sure that you're only really supposed to use the transfer function when you're not really dealing with constants -- like if you're doing fancy stuff with floating point numbers and need like really nitty gritty control over the representation thereof.
Note the variables pdirect and ndirect.
program main
integer(8), parameter :: pinfx= z'7FF0000000000000'
integer(8), parameter :: ninfx= z'FFF0000000000000'
double precision, parameter :: pinf = transfer(pinfx, 1d0)
double precision, parameter :: ninf = transfer(ninfx, 1d0)
double precision, parameter :: pdirect = z'7FF0000000000000'
double precision, parameter :: ndirect = z'7FF0000000000000'
write (*,*) 'PINFX ', pinfx
write (*,*) 'NINFX ', ninfx
write (*,*) 'PINF ', pinf
write (*,*) 'NINF ', ninf
write (*,*) 'PDIRECT', pdirect
write (*,*) 'NDIRECT', ndirect
end program
This produces the output:
PINFX 9218868437227405312
NINFX -4503599627370496
PINF +Inf
NINF -Inf
PDIRECT +Inf
NDIRECT +Inf
I hope this helps!