I have the following fortran code defined under. I am trying to change the length of the do loop if i change the value of n. When i try to compile i get the error:
‘a’ argument of ‘floor’ intrinsic at (1) must be REAL. But when i change q and w to be defined as real i get another error message. How can i fix this? q and w is clearly a integer when i use floor(...)
subroutine boundrycon(n,bc,u,v)
!input
integer :: n,bc
!output
real(8) :: u(n+2,n+2), v(n+2,n+2)
!lokale
integer :: j,i,w,q
n=30
q=floor(n/2)
w=(floor(n/2)+floor(n/6))
do j=q,w
u(q,j)=0.0;
v(q+1,j)=-v(q,j);
u(w,j)=0.0;
v(w+1,j)=-v(w,j);
end do
do i=q,w
v(i,q)=0.0;
u(i,q)=-u(i,q+1);
u(i,w+1)=-u(i,w);
v(i,w)=0;
end do
end subroutine boundrycon
Many people have already pointed this out in the comments to your question, but here it is again as an answer:
In Fortran, if you do a division of two integer values, the result is an integer value.
6/3 = 2
If the numerator is not evenly divisible by the denominator, then the remainder is dropped:
7/3 = 2
Let's look at your code:
q=floor(n/2)
It first evaluates n/2 which, since both n and 2 are integers, is such an integer division. As mentioned before, this result is an integer.
This integer is then passed as argument to floor. But floor expects a floating point variable (or, as Fortran calls it: REAL). Hence the error message:
"[The] argument of floor ... must be REAL."
So, the easiest way to get what you want is to just remove the floor altogether, since the integer division does exactly what you want:
q = n/2 ! Integer Division
If you need to make a floating point division, that is if you want two integer variables to divide into a real variable, you have to convert at least one of them to floating point before the division:
print *, 3/2 ! wrong, prints 1
print *, real(3)/2 ! right
print *, 3/2.0 ! right
print *, (3 * 1.0) / 2 ! right
print *, real(3/2) ! wrong, prints 1.0
Related
I am a university lecturer, and I will teach the Numerical Methods course this semester using Fortran 90/95 as the programming language. The beginning of the course starts with the representation of numbers, and I would like to talk about the limits of numbers that can be represented with REAL(4), REAL(8) and REAL(16). I intend to use the following code on OnlineGDB (so that students won't have to install anything on their computers, which may be a pain in times of remote learning):
Program declare_reals
implicit none
real(kind = 4) :: a_huge, a_tiny ! single precision ; default if kind not specified
!real(4) :: a ! Equivalent to real(kind = 4) :: a
a_huge = huge(a_huge)
print*, "Max positive for real(4) : ", a_huge
a_tiny = tiny(a_tiny)
print*, "Min positive for real(4) : ", a_tiny
print*,
End Program declare_reals
With this code, I get
Max positive for real(4) : 3.40282347E+38
Min positive for real(4) : 1.17549435E-38
However, if I write a_tiny = tiny(a_tiny)/2.0, the output becomes
Min positive for real(4) : 5.87747175E-39
Looking at the documentation for gfortran (which OnlineGDB uses as the f95 compiler), I had the impression that anything below tiny(x) could result in an underflow and zero would show instead of a non-zero number. Could anyone help me understand what is happening here? If tiny(x) doesn't yield the smallest positive representable number, what is being shown due to the function call?
The Fortran Standard states the following about a real value:
The model set for real x is defined by
where b and p are integers exceeding one; each fk is
a non-negative integer less than b, with f1
nonzero; s is
+1 or −1; and e is an integer that lies between some integer maximum emax and some integer minimum emin inclusively. For x = 0, its exponent e and digits fk
are defined to be zero. The integer parameters b, p,
emin, and emax determine the set of model floating-point numbers.
Real values which satisfy this definition, are referenced to be model numbers or normal floating point numbers. The floating point numbers your system can represent, i.e. the machine-representable numbers are a superset of the model numbers. They can, but not necessarily must, include the values with f1 zero — also known as subnormal floating point numbers — and are there to fill the underflow gap around zero.
The Fortran functions tiny(x), huge(x), epsilon(x), spacing(x) are all defined for model numbers.
The value of tiny(x) is given by bemin − 1, which for a single-precision floating-point number (binary32) is given by 2−126 and is the smallest model (normal) number. When your system follows IEEE754, the machine representable numbers will also contain the subnormal numbers. The smallest subnormal positive number is given bytiny(x)*epsilon(x) which in binary32 is 2−126 × 2−23. This explains why you can divide tiny(x) by two, i.e. the transition from normal to subnormal.
# smallest normal number
0 00000001 000000000000000000000002 = 0080 000016 = 2−126 ≈ 1.1754943508 × 10−38
# smallest subnormal number
0 00000000 000000000000000000000012 = 0000 000116 = 2−126 × 2−23 ≈ 1.4012984643 × 10−45
Note: when you divide tiny(x)*epsilon(x) by two, gfortran returns an arithmetic underflow error.
Ref: values taken from Wikipedia: Single precision floating-point format
In the Fortran code given below, I have made all numbers involving calculation of PI as double precision but the value of PI I get is just a real number with a large number of zero or 9 at the end. How do I make the program give PI in double precision? I am using gfortran compiler.
!This program determines the value of pi using Monte-Carlo algorithm.
program findpi
implicit none
double precision :: x,y,radius,truepi,cnt
double precision,allocatable,dimension(:) :: pi,errpi
integer :: seedsize,i,t,iter,j,k,n
integer,allocatable,dimension(:) :: seed
!Determining the true value of pi to compare with the calculated value
truepi=4.D0*ATAN(1.D0)
call random_seed(size=seedsize)
allocate(seed(seedsize))
do i=1,seedsize
call system_clock(t) !Using system clock to randomise the seed to
!random number generator
seed(i)=t
enddo
call random_seed(put=seed)
n=2000 !Number of times value of pi is determined
allocate(pi(n),errpi(n))
do j=1,n
iter=n*100 !Number of random points
cnt=0.D0
do i=1,iter
call random_number(x)
call random_number(y)
radius=sqrt(x*x + y*y)
if (radius < 1) then
cnt = cnt+1.D0
endif
enddo
pi(j)=(4.D0*cnt)/dble(iter)
print*, j,pi(j)
enddo
open(10,file="pi.dat",status="replace")
write(10,"(F15.8,I10)") (pi(k),k,k=1,n)
call system("gnuplot --persist piplot.gnuplot")
end program findpi
Your calculation is in double precision, but I see two issues:
The first is a systematic error... You determine pi by
pi(j)=(4.D0*cnt)/dble(iter)
iter is at most 2000*100, so 1/iter is at least 5e-6, so you can't resolve anything finder than that ;-)
The second issue is that your IO routines print the results in single precision! The line
write(10,"(F15.8,I10)") (pi(k),k,k=1,n)
and more specifically the format specifier "(F15.8,I10)" needs to be adjusted. At the moment it tells the compiler to use 15 characters overall to print the number, with 8 digits after the decimal point. As a first measure, you could use *:
write(10,*) (pi(k),k,k=1,n)
This uses 22 characters in total with all 15 digits for double precision:
write(10,"(F22.15,I10)") (pi(k),k,k=1,n)
The following is the code I have written to find the DFT of sine(x) over a period.
program fftw_test
implicit none
INTEGER FFTW_MEASURE
PARAMETER (FFTW_MEASURE=0)
INTEGER FFTW_ESTIMATE
PARAMETER (FFTW_ESTIMATE=64)
INTEGER FFTW_FORWARD
PARAMETER (FFTW_FORWARD=-1)
integer, parameter :: n = 8
integer :: i
double complex, dimension(0:n-1) :: input, output
double precision, parameter :: pi = 3.141592653, h = 2.0d0*pi/(n)
integer*8 :: plan
call dfftw_plan_dft_1d(plan, n, input, output, fftw_forward, fftw_measure)
do i = 0, n-1
input(i) = cmplx(sin(h*i), 0)
end do
call dfftw_execute_dft(plan, input, output)
output = output/n
output(0) = cmplx(0,0) ! setting oddball wavenumber to be 0
call dfftw_destroy_plan(plan)
do i = -n/2, n/2-1, 1
write(*, *) i, output(i+(n/2))
end do
end program
I am aware of the r2c (real to complex) function in the FFTW library. But I was advised to use the normal c2c function. So I defined the input function as a complex number with real part = sine(x) and complex part 0.
The DFT of sine(x) is supposed to be fk(-1) = cmplx(0, -0.5) and fk(1) = cmplx(0, 0.5) where fk(k) means the fourier coefficient of the k wavenumber
The output I received is as follows.
-4 ( 0.0000000000000000 , 0.0000000000000000 )
-3 ( 3.2001271327131496E-008,-0.49999998518472011 )
-2 ( -1.0927847071684482E-008, 1.4901161193847656E-008)
-1 ( -1.0145577183762535E-008, 1.4815279864022202E-008)
0 ( -1.0927847071684482E-008, 0.0000000000000000 )
1 ( -1.0145577183762535E-008, -1.4815279864022202E-008)
2 ( -1.0927847071684482E-008, -1.4901161193847656E-008)
3 ( 3.2001271327131496E-008, 0.49999998518472011 )
I am getting fk(-3) = cmplx(~0, -0.5) and fk(3) = cmplx(~0, 0.5). If I increase the grid size to 16, 32 or so I get -n/2 -1 and n/2 -1 wavenumbers with the required values instead of the -1 and 1 wavenumbers.
Does this have something to do with the way FFTW stores the output in the output array ? Or am I going wrong anywhere else ?
Also, I don't seem to be getting 'proper 0' where I should be. It is instead numbers of the order of 10^(-8) which I believe is the smallest my datatype double can hold. Is that something I should be worried about ?
Like #VladimirF already said, the ordering of the values is a bit different, than you might expect. The first half of the array holds the positive frequencies, the second half holds the negative frequencies in reverse order (see this link). And you might have to check the sign convention used by FFTW.
The problem with accuracy stems from your single precision value for pi and the use of cmplx which produces single precision complex numbers (use the keyword argument kind). In this case you could simply assign your real value to the complex variables. Applying these two changes yields a precision of ~1e-10. This can be improved by supplying a better approximation for pi (i.e. more than 10 digits).
E.g. the value pi = 3.141592653589793d0 yields results with accuracy of 1e-16.
In python, i cannot divide 5 by 22. When I try this, it gives me zero-even when i use float?!!
>>> print float(5/22)
0.0
It's a problem with order of operations. What's happening is this:
* First python takes 5/22. Since 5 and 22 are integers, it returns an integer result, rounding down. The result is 0
* Next you're converting to a float. So float(0) results in 0.0
What you want to do is force one (or both) operands to floats before dividing. e.g.
print 5.0/22 (if you know the numbers absolutely)
print float(x)/22 (if you need to work with a variable integer x)
Right now you're casting the result of integer division (5/22) to float. 5/22 in integer division is 0, so you'll be getting 0 from that. You need to call float(5)/22.
What is the best way to concatenate two integers to an integer in Fortran?
integer a = 999
integer b = 1111
integer c should be 9991111
Thanks,
SM.
Here is an example code that does what you need. It writes integers into character strings, trims and concatenetes them, and then reads the result integer from concatenated character string:
integer :: a,b,c
character(len=99) :: char_a,char_b,char_c
a = 999
b = 1111
write(unit=char_a,fmt=*)a
write(unit=char_b,fmt=*)b
char_c = trim(adjustl(char_a))//trim(adjustl(char_b))
read(unit=char_c,fmt=*)c
print*,c
end
Edit: Note that this example is general for any integer lengths, assuming they fit into their respective kind (no integer overflow).
You can use the information of the order of the number:
integer :: a = 999
integer :: b = 1111
integer :: c
c = a * 10**(ceiling(log10(real(b)))) + b
write(*,*) c
Your best bet is to use internal files to convert your two integers to a character, and then convert this back to an integer.
There is no intrinsic procedure for converting a numeric value to a character/string representation. See this discusson on Fortran Wiki for more information (see the part headed "Note").
As an example, in your case you could use the following:
program test_conversion
implicit none
integer :: a=999
integer :: b=1111
integer :: c
character(len=7) :: temp
write(temp, '(i3.3, i4.4)') a, b ! You may need to change these format specifiers
read(temp, *) c
print*, c ! This prints 9991111
end program test_conversion
You will have to change the format string if you want different widths of the character representation of your integers.