Does fortran function pass by const reference? - fortran

I'm learning how to use functions in fortran and I came across several cases which made me believe that fortran function pass the argument by const reference. When I say "pass by const reference", I'm saying it in C++ sense. I searched online and didn't find related documents. The code which makes me believe fortran functions pass arguments by const reference is as follows.
program try
implicit none
real sq
real a,b
write(*,*) sq(2)
a=2
write(*,*) sq(a)
end program
real function sq(x)
real x
sq=x**2
return
end
The output for this is
0.0000000E+00
4.000000
This result supports the idea that fortran functions pass arguments by reference, since sq(2) doesn't work. After this code, I put a new line x=x+1 inside the definition of sq. The code looks like
program try
implicit none
real sq
real a,b
write(*,*) sq(2)
a=2
write(*,*) sq(a)
end program
real function sq(x)
real x
x=x+1
sq=x**2
return
end
This code does compile, but when I run it, it gives the following error
forrtl: severe (180): SIGBUS, bus error occurred
Image PC Routine Line Source
a.out 00000001000014DB Unknown Unknown Unknown
a.out 000000010000144C Unknown Unknown Unknown
Stack trace terminated abnormally.
I guess I got this error because I can't modify the argument inside the function definition, which makes me believe that the argument is passed by const reference. The compiler I'm using is ifort 12.0.0. I'm running it on Mac OS X 10.6.8. Can anyone tell me whether my guess is true?
Update: According to the comment of #Jean, after modifying sq(2) to sq(2.0). The first example will work, the second one still gives the same error. The modified version of the first example is
program try
implicit none
real sq
real a,b
write(*,*) sq(2.0)
a=2
write(*,*) sq(a)
end program
real function sq(x)
real x
sq=x**2
return
end
The output is
4.000000
4.000000
I don't know why this simple modification will work. Hopefully someone can clarify for me.

As pointed out in the comments, you should use explicit interfaces. Then the compiler is able to check argument types. There are different possibilities to do that. For larger programs use modules, for smaller ones, you can include your procedure in the main program by using the contains keyword.
Here is a slightly modified version of your code:
program try
implicit none
real a,b
write(*,*) sq(2.0)
a=2
write(*,*) sq(a)
contains
real function sq(x)
real, value :: x
x=x+1
sq=x**2
return
end
end program
What's new?
the function is included in the main program with the contains keyword. When doing so, you don't have to declare sq like you did before in your third line. Also, the compiler can now check the argument type. Try to write 2 instead of 2.0 and see what happens.
You are right about the references. In Fortran arguments are passed by reference. If your argument is not a variable but just a number, then you can not change it within the procedure because it is constant. If you want variables to be passed by value, use the value keyword.

Related

gfortran compiler error calling recursive log_gamma function

I uploaded a 2F1 hypergeometric function but it turns out that it does not compile on my computer. It is from this article. I use
GNU Fortran (Built by Jeroen for the R-project) 8.3.0
shiped with RTools 4.0. Can you figure out why it does not compile and how this could be solved? In the code below, I just kept the one line that generates the error. The error is given next.
MODULE HYP_2F1_MODULE
!--------------------------------------------------------------------
IMPLICIT NONE
INTEGER, PARAMETER :: PR=KIND(1.0D0)
REAL(PR) :: ONE=1.0D0
CONTAINS
!
END MODULE HYP_2F1_MODULE
!
!----------------------------------------------------------------------
RECURSIVE FUNCTION LOG_GAMMA(Z) RESULT(RES)
USE HYP_2F1_MODULE
IMPLICIT NONE
COMPLEX(PR),INTENT(IN) :: Z
COMPLEX(PR) :: RES
!
RES = LOG_GAMMA( ONE -z);
!
END FUNCTION LOG_GAMMA
Here is the error message
testZ.f90:18:22:
RES = LOG_GAMMA( ONE - Z);
1
Error: 'x' argument of 'log_gamma' intrinsic at (1) must be REAL
There is a Fortran 2008+ intrinsic of the same name
16.9.119 LOG_GAMMA (X)
1 Description. Logarithm of the absolute value of the gamma function.
2 Class. Elemental function.
3 Argument. X
shall be of type real. Its value shall not be a negative integer or
zero.
There is some sort of clash I do not completely understand here. The obvious workaround is to rename your function. What I can say is that without the result clause (which you need because of the recursive attribute) the intrinsic would be shadowed. It might be a compiler bug.
Also consider, whether the intrinsic of the same name couldn't also do what you need from your function.
In addition to #VladimirF's suggestions, you can add the compiler flag -std=f95 to enforce compliance with the Fortran 95 standard. Provided you haven't added -fall-intrinsics this will disable all intrinsics from standards after Fortran 95, and should make the legacy code work.

Why do I always get the same result when using function and contains in fortran

I'm trying to do a basic calculation by calling a function using contains
Program main
implicit none
integer*8 Nmax,i
Parameter (Nmax=5)
real*8 x, f(Nmax), n
do i=1, Nmax
n=i
f=func(n,Nmax)
write(*,*) f(i)
end do
Contains
real*8 function func(x,Nmax)
integer*8 Nmax,i
real*8 x, f(Nmax)
do i=1, Nmax-1
f(i)=i**2d0-4d0*i-7d0
end do
end function
end program main
I get this result:
-9.255963134931783E+061
-9.255963134931783E+061
-9.255963134931783E+061
-9.255963134931783E+061
-9.255963134931783E+061
I think I'm making the wrong variable definitions. Thank you for your help.
There are multiple problems with your program.
First, you probably meant to write:
f(i)=func(n,Nmax)
in the main program. Without the subscript you assign the same value to each element of the array. You might think that explains the results, but it doesn't as you'd still see what you expect.
Another problem is highlighted by the following warning I get when I compile your code with Intel Fortran:
t.f90(14): warning #6178: The return value of this FUNCTION has not been defined. [FUNC]
real*8 function func(x,Nmax)
-------------------^
You never assign the value of func, so you get whatever garbage happens to be in the return register.
The function you have isn't really what you want, either. You probably want one that computes and returns a scalar (single) value and hence there is no need for an array inside func.
A third problem is that func is ignoring the n argument (which, contrary to convention, you have declared as a real.)
If you want a loop in the main program, have the function compute and return a single result based on the argument passed to it. There is no need to pass both the loop index and nmax each time. Other options, slightly more advanced, would be to keep the array assignment in the main program but do away with the loop there and either have the function return an array or make the function ELEMENTAL. I will leave it as an exercise for you once you figure out what you really intend here.
Lastly, I would discourage you from using nonstandard syntax such as "real*8". Please learn about KIND specifiers and the SELECTED_REAL_KIND intrinsic function.

Fortran Type mismatch in argument n at 1 passed REAL(8) to integer (4)

I'm having a problem with a fortran 77 project (Yes i know it's archaic, but my prof requres us to program in fixed form fortran)
So I'm having a problem with a subroutine, which should read an N dimension Vector, which should be a column. the code for this looks like: (its still an early draft for my homework, just trying to figure out how to call a subroutine the rest of the code will be done if i can compile this problem)
Program gauss
implicit double precision (A-H,O-Z)
!he directly asked for implicit typing
call Vread(V(N))
end program
Subroutine Vread(V,N)
Implicit double precision (A-H,O-Z)
dimension V(N)
read(*,*) (V(I),I=1,N)
return
end
So my problem is: If I try to compile it with gfortran gauss.exe -o gauss.f the compiler returns with error:
Type mismatch argument 'n' at(1); passed REAL(8) to Integer(4)
In your main program you write
call Vread(V(N)) ! this passes a rank-1 vector with N elements
but your subroutine is declared
Subroutine Vread(V,N) ! this requires 2 arguments
Change the call to
call Vread(V,N)
and let your professor know that the 21st Century arrived a while age. Note too that it is possible to write 21C Fortran in fixed-form. It doesn't make much sense, but might enable you to toe the line while developing in a more modern version of the language.
!he directly asked for implicit typing
I trust that you are in a jurisdiction where you do not pay directly for your education. If you are paying fees then demand more, you're being cheated.

FORTRAN 95: OPTIONAL statement does not work (using FTN95 and Plato)

I am having a problem with the OPTIONAL statement in functions and subroutines with Fortran 95. Currently I am using Silverfrost's Plato and their FTN95 compiler (in "Release Win32" mode). After trying to implement the OPTIONAL statement in a more complex program I am writing, I created a very simple program to test it. Here is the code:
program TEST
implicit none
integer :: a=1, b=2
call Z(a,b)
call Z(a)
call Z(b)
end program TEST
subroutine Z(x,y)
implicit none
integer :: x
integer, optional :: y
if (present(y)) then
write(*,*) x, y
else
write(*,*) x
endif
end subroutine Z
I expected the following result displayed on the screen:
1 2
1
2
Well, the code compiles, although I get a warning (673) that "SUBROUTINE Z has been called with too few arguments". After executing it, I get on my screen:
1 2
and then an "Access violation" error message. Can somebody understand what is wrong here?
Thanks a lot!
Gilberto
Try putting the subroutine in a module, like so:
module testoptional
contains
subroutine Z(x,y)
implicit none
integer :: x
integer, optional :: y
if (present(y)) then
write(*,*) x, y
else
write(*,*) x
endif
end subroutine Z
end module testoptional
program TEST
use testoptional
implicit none
integer :: a=1, b=2
call Z(a,b)
call Z(a)
call Z(b)
end program TEST
Compiling and running then gives the expected result using gfortran and ifort.
The problem is in how the main program knows (or guesses) the interface to Z(x,y). In the code you provide, although the main program and the subroutine are in the same file, there's nothing explicitly telling the main program the interface - the calling sequence, including argument counts, types, etc - of Z. The first call is to Z(a,b), so it infers there's a subroutine out there somewhere that takes two parameters; then it tries calling it with one parameter, which fails.
Putting the subroutine in a module, then using the module (you could also use contains for a contained subroutine, or explicitly/manually providing the main program the interface using an interface block) then gives the main program the information it needs about the calling sequence - e.g., that there's an optional argument - and things work correctly.
Procedures with optional arguments must have explicit interfaces, so try giving one to Z. (E.g. just use the subroutine from a short module.)
I don't have this specific compiler on hand, but I've gotten an error when using the Cray compiler for cases like this.

Why does this Fortran random number generator cause a segmentation fault?

I took the following function ran0 from the text Numerical Recipes. I wrote my own program random2 to call ran0.
Why does this code cause a segmentation fault? Thanks for your time.
FUNCTION ran0(idum)
INTEGER idum,IA,IM,IQ,IR,MASK
REAL ran0,AM
PARAMETER (IA=16807,IM=2147483647,AM=1./IM,IQ=127773,IR=2836,MASK=123459876)
INTEGER k
idum=ieor(idum,MASK)
k=idum/IQ
idum=IA*(idum-k*IQ)-IR*k
if (idum.lt.0) idum=idum+IM
ran0=AM*idum
idum=ieor(idum,MASK)
return
END FUNCTION
PROGRAM random2
IMPLICIT NONE
REAL :: ran0
PRINT *, ran0(6)
END PROGRAM
You pass the constant 6.0 to your function as the IDUM dummy argument. You then (attempt to) modify this argument with lines such as idum = ieor(...) etc. You are effectively trying to modify a constant.
The value of 6.0 has been fixed for some time now - long enough that most programmers expect to find it somewhere between 5.0 and 7.0. Please don't try and change it.
Extending the answer of IanH, if you partially rewrite this in more modern Fortran:
module my_subs
contains
FUNCTION ran0(idum)
INTEGER, intent(inout) :: idum
INTEGER IA,IM,IQ,IR,MASK
REAL ran0,AM
PARAMETER (IA=16807,IM=2147483647,AM=1./IM,IQ=127773,IR=2836,MASK=123459876)
INTEGER k
idum=ieor(idum,MASK)
k=idum/IQ
idum=IA*(idum-k*IQ)-IR*k
if (idum.lt.0) idum=idum+IM
ran0=AM*idum
idum=ieor(idum,MASK)
return
END FUNCTION
end module my_subs
PROGRAM random2
use my_subs
IMPLICIT NONE
!REAL :: ran0
PRINT *, ran0(6)
END PROGRAM
Identifying the argument as being both input and output with the intent(inout) attribute and placing the subroutine in a module and using that module to allow the compiler to check the consistency of the arguments, the compiler is likely to find this problem. For example, gfortran outputs:
PRINT *, ran0(6)
1
Error: Non-variable expression in variable definition context (actual argument to INTENT = OUT/INOUT) at (1)