When I run my code with Silverfrost fortran, the result is -2.987531633638E-02. But with gfortran (under cygwin and ubuntu linux) it is -2.9875316336381942E-002. My code is here:
program seebeck
real*8::ss
integer::ix,i
complex,parameter::i1=(0.0,1.0)
call quad3d(0.,190.,ss)
write(*,*) "result:",ss
stop
end program seebeck
SUBROUTINE quad3d(x1,x2,ss)
REAL:: x1,x2
external f1
real*8::ss,f1
call qgausep(f1,x1,x2,ss)
return
END
SUBROUTINE qgausep(f2,a,b,ss)
external f2
REAL:: a,b
real*8::ss,f2
INTEGER j
REAL*8 dx,xm,xr,w(5),x(5)
SAVE w,x
DATA w/.2955242247,.2692667193,.2190863625,.1494513491,.0666713443/
DATA x/.1488743389,.4333953941,.6794095682,.8650633666,.9739065285/
xm=0.5*(b+a)
xr=0.5*(b-a)
ss=0
do 11 j=1,5
dx=xr*x(j)
ss=ss+w(j)*(f2(xm+dx)+f2(xm-dx))
11 continue
ss=xr*ss
return
END
function f1(t)
real*8::t,f1
f1=cos(t)/(1+exp(t))**2
end function
There is a huge difference between the two results. I cannot explain the cause of this difference as a floating point inaccuracy.
Note: My code is a draft version and has no physical meaning.
This has something to do with how different compilers handling your data assignment at line 26/27 of your code. You defined both w and x as double-precision arrays, but only initialize them with lower precision values. This will introduce some floating point inaccuracy. In fact if I pass your code through the NAG compiler (which is known to be very strict), it gives a warning:
Warning: test.f90, line 26: Low-precision data-value assigned to high-precision data-object
To confirm, you can print out the values in array w and x to see if they are different when using different compilers.
Related
This question already has answers here:
Modern Fortran equivalent of an action statement shared by nested DO and GO TO
(1 answer)
Function has no implicit type
(4 answers)
Closed 1 year ago.
I am trying to calculate the moment of inertia in fortran. The formula I am using is following: The code I am using:
program moment
implicit none
real :: cR,h,rho0,a,b,c,d,resultV,pi,resultMI,aMass,exactresMI,exactresV,r,res,z,rho
integer :: m,n
! rho0 = density, cR=capital R( radius),h= height )
rho0=10000
cR=0.05
h=0.1
a=0.d0
b=h
c=0.d0
d=cR
m=1000
n=1000
call cheb2(a,b,m,c,d,n,funV,res)
pi=4*datan(1.d0)
resultV=res*2*pi
exactresV= pi/3*cR**2*h
write(*,*)
write(*,*) "Numerical volume result =", resultV
write(*,*) "Exact volume result = ",exactresV
call cheb2(a,b,m,c,d,n,funV,res)
resultMI=res*2*pi
aMass=exactresV*rho0
exactresMI=3/10.*aMass*cR**2
write(*,*)
write(*,*) "Numerical Moment of Inertia result =", resultMI
write(*,*) "Exact Moment of Inertia result = ",exactresMI
end program
function funV(z,r)
if (r.gt.z*cR/h) then
rho=0.d0
else
rho=1.d0
end if
funV=rho*r
return
end
function funMI(z,r)
if (r.gt.z*cR/h) then
rho=rho0
else
rho=1.d0
endif
funMI=rho*r**3
return
end
include "CHEB.FOR"
Our instructor does not use "implicit none" , so I am really new to this operator. Out instructor gave us CHEB.FOR code for calculating 2 dimensional integrals. I am writing it here:
subroutine ch4xy(al,bl,cl,dl,f,ri)
implicit double precision (a-h,o-z)
common/ttxy/ t1,t2
dimension xx(4),yy(4)
c1=(al+bl)/2.d0
c2=(dl+cl)/2.d0
d1=(-al+bl)/2.d0
d2=(dl-cl)/2.d0
xx(1)=c1+d1*t1
xx(2)=c1+d1*t2
yy(1)=c2+d2*t1
yy(2)=c2+d2*t2
xx(3)=c1-d1*t1
xx(4)=c1-d1*t2
yy(3)=c2-d2*t1
yy(4)=c2-d2*t2
ss=0
do 3 i=1,4
do 3 j=1,4
ss=ss+f(xx(i),yy(j))
3 continue
ri=ss*d1*d2/4.d0
return
end
subroutine cheb2(a,b,m,c,d,n,f,r)
implicit double precision (a-h,o-z)
external f
common/ttxy/ t1,t2
t1=0.187592
t2=0.794654
hx=(b-a)/m
hy=(d-c)/n
rr=0
do 5 i=1,m
do 5 j=1,n
aa=a+(i-1)*hx
bb=aa+hx
cc=c+(j-1)*hy
dd=cc+hy
call ch4xy(aa,bb,cc,dd,f,ri)
rr=rr+ri
5 continue
r=rr
return
end
When I compile the file, a couple of errors and a warning appear:
CHEB.FOR:19:17:
19 | do 3 j=1,4
| 1
Warning: Fortran 2018 deleted feature: Shared DO termination label 3 at (1)
CHEB.FOR:36:11:
36 | do 5 j=1,n
| 1
Warning: Fortran 2018 deleted feature: Shared DO termination label 5 at (1)
momentOFinertia.f95:17:27:
17 | call cheb2(a,b,m,c,d,n,funV,res)
| 1
Error: Symbol 'funv' at (1) has no IMPLICIT type
First, I dont understand why funV is unclassifiable statement, it classifies as a function. Second, our instructor used some old operations which is apparently not valid in new fortran. I dont know what could replace "shared do".
The main problem (fixed by your edit)is that your code misses an end or end program at the end. Alternatively, you could put an end program after your functions and contains between the subroutine and the main program body. That would make the functions to be internal subprograms and would fix your other problem - no implicit type for the functions.
This - putting the functions inside the program as internal subprograms, allows the program to "see" them and then the program can correctly pass them to other procedures. Alternatively, you could make an interface block for them or declare their type and let them be external. See Why do I have to specify implicitly for a double precision return value of a function in Fortran? for more.
You also have a type mismatch. The code you got from your instructor uses double precision but your code uses the default real. You have to synchronize that. Update your code to double precision, either using double precision or using real kinds.
The compiler also warns you that your program is using deleted features. These features were deleted in modern revisions of the Fortran standards. However, the compiler remain largely backwards compatible and will compile the code including those features anyway, unless you request strictly a certain standard revision.
In this case two do-loops use one common continue statement
do 5 ...
do 5 ...
5 continue
This is not allowed and can be fixed by inserting another continue with another label or, better, by using end do.
If you have a standalone function in a file (not in a module) and you call it with a single-precision while it expects a double precision number:
main.f90 :
program main
call test(1.0)
end program main
test.f90:
subroutine test(a)
double precision :: a
print *, "a", a
end subroutine
How does the compiler "casts" from single to double precision in this case ?
Using floating-point format, I would expect the bits to stay the same during the cast but additionnal zeroes to be appended. That is:
1 = 0 01111111 00000000000000000000000 in single-precision
and I would expect the final value to be 2^(-7):
0 01111111000 0000000000000000000000000000000000000000000000000000 in double precision
Surprisingly, with gfortran 6.4.0, the final value is 5.2635442471208903E-315.
The compiler does no casting. What you have written is not Fortran.
In the main program the subroutine test has an implicit interface. Essentially, the compiler knows nothing about this except that it is a subroutine. You also tell it that it has a single (default) real argument.
It is your responsibility, not the compiler's, to provide the correct type and kind of argument when referencing the subroutine. You failed there, so you do not have a compliant Fortran program. The Fortran compiler owes you nothing.
What you will observe will depend on implementation details of the Fortran processor. The subroutine expects a double precision argument and has no reason to believe it has anything else. Whether there is copy-in/copy-out or some address passing1, the interpretation of memory will not match. In the dummy argument, all but the bytes corresponding to the default real of the actual argument are "junk".
If you provide an explicit interface for the subroutine in the main program there will still be no casting, but the compiler will notice the mismatch. Equally, some compilers (perhaps with certain compile flags) will do some checking even when an implicit interface is there.
1 For details of the likely pass-by-reference, see the comment by user5713492.
I guess the compiler "casts" according to endianess. If you stak zeros on the left, you get:
0 00000000000 0000000000000000000000111111100000000000000000000000
Which is 5.2635442471208903E-315. You can check by forcing endianess at compile-time.
I installed gfortran on my Ubuntu 15.04 system. While compiling Fortran code, the DO loop asks to take integer parameters only and not real values or variables. That includes the loop variable and the step expression. Why can't it take real values too?
The following is a program taken from here, exercise 3.5 of the section nested do loops.
program xytab
implicit none
!constructs a table of z=x/y for values of x from 1 to 2 and
!y from 1 to 4 in steps of .5
real :: x, y, z
print *, ' x y z'
do x = 1,2
do y = 1,4,0.5
z = x/y
print *, x,y,z
end do
end do
end program xytab
The error shown after compiling is:
xytab.f95:8.4:
do y = 1,4,0.5
1
Warning: Deleted feature: Loop variable at (1) must be integer
xytab.f95:8.12:
do y = 1,4,0.5
1
Warning: Deleted feature: Step expression in DO loop at (1) must be integer
xytab.f95:7.3:
do x = 1,2
1
Warning: Deleted feature: Loop variable at (1) must be integer
The Fortran standard now requires that a do construct's loop control is given by (scalar) integer expressions and that the loop variable is a (scalar) integer variable. The loop control consists of the start, step, and stop expressions (your step expression is 0.5). See R818 and R819 (8.1.6.2) of the Fortran 2008 document. That, then, is the short and simple answer: the standard says so.
It's a little more complicated than that, as the messages from the compiler suggest. Using other forms for loop control was present in Fortran up until Fortran 95. That is, from Fortran 95 onward using real expressions is a deleted feature.
What harm is there in using real expressions? Used correctly, one could imagine, there is no harm. But there's real difficulty in portability with them.
Consider
do x=0., 1., 0.1
...
end do
How many iterations? That would be (under the rules of Fortran 90) MAX(INT((m2 – m1 + m3) / m3), 0) where (m1 is the start value (0.), m2 the stop value (1.) and m3 the step value (0.1)). Is that 10 or 11 (or even 9)? It depends entirely on your numeric representation: we recall that 0.1 may not be exactly representable as a real number and INT truncates in converting to integer. You'd also have to worry about repeated addition of real numbers.
So, use integers and do some arithmetic inside the loop
do y_loop = 0, 6
y = 1 + y_loop/2.
...
end do
or
y = 1
do
if (y>4) exit
...
y = y+0.5
end do
Finally, you mention .f90 and .f95 file suffixes. gfortran doesn't take the first to mean that the source code follows the Fortran 90 standard (where the code would be fine). Further, the messages from the compiler are merely warnings, and these can be suppressed using the -std=legacy option. Conversely, using -std=f95 (or later standards) these become errors.
As a bonus fun fact consider the following piece of Fortran 90 code.
real y
integer i
loop_real: do y=1, 4, 0.5
end do loop_real
loop_integer: do i=1, 4, 0.5
end do loop_integer
While the loop named loop_real is valid, that named loop_integer isn't. In the calculation of the iteration count the three expressions are converted to the kind, with kind parameters, of the loop variable. INT(0.5) is 0.
I use the Intel Visual Fortran. The problem I'm dealing with has many PARAMETERS and I want to avoid repetitious declaration and initiation of them at the beginning of each routine.
Is there any way for initialization these PARAMETERS using MODULES?
Of course the answer is YES, but some of intrinsic function are not accepted in the initiation statement. Here is a simple sample code,
MODULE mod
IMPLICIT NONE
REAL, PARAMETER :: x = SQRT ( 4 )
END MODULE
PROGRAM prog
USE mod
IMPLICIT NONE
PRINT *, x
END PROGRAM
Once compiled, the compiler shows me this error :
Error #6362: The data types of the argument(s) are invalid.
[SQRT] Source1.f90
Albeit using ABS ( ) is not followed by this error, but SIN ( ) does.
The problem is that the sqrt (and sin) intrinsic requires a floating point argument. There just happens to be both floating point and integer implementations of abs.
Replace 4 with 4. and it should work.
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.