Fortran DO loop, warning to use integer only - fortran

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.

Related

Moment of inertia in fortran [duplicate]

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.

A real variable may be unchanged after adding a small value?

I just wanted a second opinion. I am newer to gfortran and I have this code:
program Assignmenttwo
!Nik Wrye
!CDS251-001
!Homework #2/Assignment #2
!September 9th, 2021
!This program is to Next, write a do loop that iterates 10 million
!times. In the loop, add 1.e-7 (one ten-millionth) to each variable (The variable should
!appear on both sides of the equal sign.) After the loop, print out each variable with
!a label.
implicit none
!declaring variables
real*4:: Numone, Numtwo
!intializing variables
Numone = 1.0
Numtwo = 2.0
do while (Numone < 1.1.and.Numtwo<2.1)!this do statement will cycle the loop until it has it the 10 millionth time
Numone = Numone+1.e-7 !adding the desired amount
Numtwo = Numtwo+1.e-7
enddo
print*, Numone
print*, Numtwo
end program Assignmenttwo
I am expecting the output to be 1.1 and 2.1 but I am getting 1.1 and 2.0. Any ideas?
Arguably the point of this assignment is that you get 1.1 but 2.0. This is down to the behaviour of floating point, the full details of which you can read about elsewhere.
You can do the simple test
print *, 1.+1e-7, 2.+1e-7
print *, 1.+1e-7/=1., 2.+1e-7/=2.
without doing the loops to see the effect.
Instead of repeating massive detail about the mechanics, I'll just mention that you can confirm that 1e-7 is "too small" to have an effect by using the nearest intrinsic function
print *, nearest(2., 1.)
With double precision you'll get different answers. (And this is why careful consideration of which real type to use in any case is important.)
Don't forget that Fortran no longer allows real DO control where increments may indeed be too small to have an effect.

Use of arithmetic if in fortran 77

I have a question about arithmetic if in f77. If I get it properly it was supposed to be used that way:
if(integer) st-,st0,st+
and meant that st- was done if the integer was <0, st0 was done if integer = 0, and st+ for integer > 0.
I have a case like this:
IF(number) test=0
I am right assuming test=0 statement will be done if number is lower than 0?
Thanks
Your example code is a normal logical if, but with an integer instead of a logical expression for the condition. Some compilers (Intel and predecessors - DEC, Compaq) do allow that as a non-standard extension, gfortran does not. As far as I know not even with an option like -fdec.
See Implicit conversion integer <--> logical in Fortran if statement for more.
What an arithmetic if does is that it selects one of the three branches using numeric statement labels, you cannot put executable statements after an arithmetic if.
if(integer) label-,label0,label+
That means, e.g.,
if (i) 10, 20, 30
10 do something
20 do something else
30 do something else

How to assign bit-pattern Z'FEDCBA09' to a 32bit integer

How can I assign the boz-literal-constant Z'FEDCBA09' or any other bit-pattern with the most-significant bit equal to 1 to an integer?
The standard states:
INT(A[,KIND]): If A is a boz-literal-constant, the value of the result is the value whose bit sequence according to the model in 16.3 is the same as that of A as modified by padding or truncation according to 16.3.3. The interpretation of a bit sequence whose most significant bit is 1 is processor dependent.
source: Fortran 2018 Standard
So the following assignments might fail (assume integer is default 32 bit):
program boz
implicit none
integer :: x1 = int(Z'FEDCBA09')
integer :: x2 = int(Z'FFFFFFFF')
integer :: x3
data x3/Z'FFFFFFFF'/
end program
Using gfortran, this will only work when adding -fno-range-check but this introduces extra unwanted effects:
-fno-range-check: Disable range checking on results of simplification of constant expressions during compilation. For example, GNU Fortran will give an error at compile time when simplifying a = 1. / 0. With this option, no error will be given and a will be assigned the value +Infinity. If an expression evaluates to a value outside of the relevant range of [-HUGE():HUGE()], then the expression will be replaced by -Inf or +Inf as appropriate. Similarly, DATA i/Z'FFFFFFFF'/ will result in an integer overflow on most systems, but with -fno-range-check the value will "wrap around" and i will be initialized to -1 instead.
source: GNU Compiler Collection, gfortran manual
I attempted the following, which works fine but still not 100%
integer(kind=INT32) :: x1 = transfer(real(Z'FEDCBA09',kind=REAL32),1_INT32)
integer(kind=INT32) :: x1 = transfer(real(Z'FFFFFFFF',kind=REAL32),1_INT32)
The latter case fails with gfortran as it complains that Z'FFFFFFFF' represents NaN.
Using IOR(0,Z'FEDCBA09') also fails as it converts the boz-literal using INT
Question: How can you robustly assign a bit pattern using a boz-literal-constant? That is to say, independent of the used compiler (GNU, SUN, PGI, NAG, ...).
Answer: The most robust answer is currently given by Jim Rodes in this comment:
x = ior(ishft(int(Z'FEDC'),bit_size(x)/2),int(Z'BA09'))
This will work on any compiler and does not require any other data-type to be successful.
The need for -fno-range-check has been removed in what will be gfortran 10.1 when it is released. In 10.1, the bit patterns you have specified will be treated as if they are 32-bit unsigned integers and twos-complement wrap-around semantics are enforced.
Your first code snippet with a print statement added
program boz
implicit none
integer :: x1 = int(Z'FEDCBA09')
integer :: x2 = int(Z'FFFFFFFF')
integer :: x3
data x3/Z'FFFFFFFF'/
print *, x1, x2, x3
end program
yields
$ gfortran -o z file.f90
$ ./z
-19088887 -1 -1
and does not require the -fno-range-check option. The same goes for the proposed transfer method:
program boz
use iso_fortran_env
implicit none
integer(kind=INT32) :: x1 = &
& transfer(real(Z'FEDCBA09',kind=REAL32),1_INT32)
integer(kind=INT32) :: x2 = &
& transfer(real(Z'FFFFFFFF',kind=REAL32),1_INT32)
print '(I0,1X,Z8.8)', x1, x1
print '(I0,1X,Z8.8)', x2, x2
end program
returning:
$ gfortran -o z file.f90
$ ./z
-19088887 FEDCBA09
2143289344 7FC00000
Note: gfortran converts sNaN into qNan, which is a bug but no one cares.
If you are stuck with an older version of gfortran, then with the
integer case you need to use an intermediate conversion
program boz
use iso_fortran_env
implicit none
integer(kind=INT32) :: x1 = &
& transfer(int(Z'FEDCBA09',kind=INT64),1_INT32)
print '(I0,1X,Z8.8)', x1, x1
end program
gfortran will constant fold the statement with transfer. You can verify this by looking at the file created with the -fdump-tree-original option. For both this answer and the previous one, the command line is simple gfortran -o z file.f90.
When dealing with languages that do not support unsigned integers and you need to be able to test and/or set the high bit of the largest available integer, you can split the value into 2 variables and deal with the high order and low order bits separately.
One method would be to put the upper half into one variable and the lower half into another so that:
integer :: x1 = int(Z'FEDCBA09')
becomes:
integer :: x1Hi = int(Z'FEDC')
integer :: x1Lo = int(Z'BA09')
As the OP pointed out in an edit, a shift operation could then be used to assign the full value to a single variable like this. I changed it slightly so that it would work in case x is more than 32 bits.
x = ior(ishft(int(Z'FEDC'), 16), int(Z'BA09'))
Another possible method would be to have a separate variable for just the high bit.
I have asked a similar question before at comp.lang.fortran: https://in.memory.of.e.tern.al/comp.lang.fortran/thread/3878931
A practically usable, even though still the 100% probability was still questioned by some (see there) was just to use the reverse BOZ constant/string and NOT() it.
Instead of
integer, parameter :: i = Z'A0000000'
use
integer, parameter :: i = NOT(int(Z'5FFFFFFF'))
The analysis in the link goes to a large detail and to fine points of the standard and the numeric model interpretation.
Since then I use this in my production code: https://bitbucket.org/LadaF/elmm/src/master/src/rng_par_zig.f90 line 285 which is a translation of http://vigna.di.unimi.it/xorshift/xorshift128plus.c

Why are resulsts of silverfrost and gfortran different?

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.