Why do I keep getting 0 as output? [duplicate] - fortran

This question already has an answer here:
Why are the elements of an array formatted as zeros when they are multiplied by 1/2 or 1/3?
(1 answer)
Closed 5 years ago.
Why does this fortran program produce only zeros? When I print it out i get -0.00000 everywhere! What have I done wrong? In matlab it runs perfectly. I dont see any reason why its not working to be honest!
It seems like its the fraction that messes it up. if I set x equal to some decimal number it works.
program main
implicit none
integer iMax, jMax
double precision, dimension(:,:), allocatable :: T
double precision x, dx,f,L2old,L2norm,y
integer i, j,n,bc
n=10
allocate(T(1:n+2, 1:n+2))
T=0.0d0
do i=2,n+1
do j=2,n+1
x=(j+1)*1/24
y=(i+1)*1/24
T(i,j)= -18*(x**2+y**2)**2
Write(*,*)'T(',i,'',j,'', T(i,j)
end do
end do
Write(*,*)'T(1,1)',T(1,1)
end program main

x=(j+1)*1/24
1/24 is an integer division that rounds down to 0. You should be able to force floating point division by making at least one of the operands floating point,
e.g.
x=(j+1)*1.0/24.0

As was indicated by Jim Lewis, the answer to the OP's question was indeed the integer division used.
Nonehteless, I think it is important to point out that one should take care of how the floating point fraction is written down. As the OP's program shows, x was of type DOUBLE PRECISION. Then the correct result should be
x=(j+1)*1.0D0/24.0D0
The difference here is that now you ensure that the division happens with the same precision as x was declared.
To following program demonstrates the problem ::
program test
WRITE(*,'(A43)') "0.0416666666666666666666666666666666..."
WRITE(*,'(F40.34)') 1/24
WRITE(*,'(F40.34)') 1.0/24.0
WRITE(*,'(F40.34)') 1.0D0/24.0
WRITE(*,'(F40.34)') 1.0D0/24.0D0
end program test
which as the output
0.0416666666666666666666666666666666...
0.0000000000000000000000000000000000
0.0416666679084300994873046875000000
0.0416666666666666643537020320309239
0.0416666666666666643537020320309239
You clearly see the differences. The first line is the mathematical correct result. The second line is the integer division leading to zero. The third line, shows the output in case the division is computed as REAL while the fourth and fifth line are in DOUBLE PRECISION. Please take into account that in my case REAL implies a 32bit floating point number and DOUBLE PRECISION a 64 bit version. The precision and representation of both REAL and DOUBLE PRECISION is compiler dependent and not defined in the Standard. It only requires that DOUBLE PRECISION has a higher precision than REAL.
4.4.2.3 Real type
1 The real type has values that approximate the mathematical real numbers. The processor shall provide two or more approximation methods that define sets of values for data of type real. Each such method has a representation method and is characterized by a value for the kind type parameter KIND. The kind type parameter of an approximation method is returned by the intrinsic function KIND (13.7.89).
5 If the type keyword REAL is used without a kind type parameter, the
real type with default real kind is specified and the kind value is
KIND (0.0). The type specifier DOUBLE PRECISION specifies type real
with double precision kind; the kind value is KIND (0.0D0). The
decimal precision of the double precision real approximation method
shall be greater than that of the default real method.
This actually implies that, if you want to ensure that your computations are done using 32bit, 64bit or 128bit floating point representations, you are advised to use the correct KIND values as defined in the intrinsic module ISO_FORTRAN_ENV.
13.8.2.21 REAL32, REAL64, and REAL128
1 The values of these default integer scalar named constants shall be
those of the kind type parameters that specify a REAL type whose
storage size expressed in bits is 32, 64, and 128 respectively. If,
for any of these constants, the processor supports more than one kind
of that size, it is processor dependent which kind value is provided.
If the processor supports no kind of a particular size, that constant
shall be equal to −2 if the processor supports kinds of a larger size
and −1 otherwise.
So this would lead to the following code
PROGRAM main
USE iso_fortran_env, ONLY : DP => REAL64
IMPLICIT NONE
...
REAL(DP) :: x
...
x = (j+1)*1.0_DP/24.0_DP
...
END PROGRAM main

Related

Fortran: inexact value read from a text file [duplicate]

In the code below I am adding together 865398.78 and -865398.78. I expect to get 0, but instead I get -0.03.
Source Code:
program main
real(8) :: x
open(10,file="test.txt")
read(10,*)x
print *,"x=",x
x=x+865398.78
print *,"x+865398.78=",x
end program
Result:
x= -865398.780000000
x+865398.78= -3.000000002793968E-002
Am I wrong with the usage of "read" codes or something else?
The number 865398.78 is represented in single precision in your code. Single precision can handle about 7 significant digits, while your number has 8. You can make it double precision by writing
x=x+865398.78_8
I will make one big assumption in this answer: that real(8) corresponds to double precision.
You are probably assuming that your 865398.78 means the same thing wherever it occurs. In source code that is true: it is a default real literal constant which approximates 865398.78.
When you have
x=x+865398.78
for x double precision, then the default real constant is converted to a double precision value.
However, in the read statement
read(10,*)x
given input "-865398.78" then x takes a double precision approximation to that value.
Your non-zero answer comes from the fact that a default real/single precision approximation converted to a double precision value is not in general, and isn't in this case, the same thing as an initial double precision approximation.
This last fact is explained in more detail in other questions. As is the solution to use x=x+865398.78_8 (or better, don't use 8 as the kind value).

How to set Integer and Fractional Precision independently?

I'm learning Fortran(with the Fortran 2008 standard) and would like to set my integer part precision and decimal part precision for a real variable independently. How do i do this?
For example, let us say that i would like to declare a real variable that has integer part precision as 3 and fractional part precision as 8.
An example number in this above specification would be say 123.12345678 but 1234.1234567 would not satisfy the given requirement.
Fortran real numbers are FLOATING point numbers. Floating point numbers do not store the integer part and the decimal part. They store a significand and an exponent.
See how floating point numbers work http://en.wikipedia.org/wiki/Floating-point_arithmetic There is usually one floating point format which your CPU uses and you cannot simply choose a different one.
What you are asking for is more like the FIXED point arithmetic, but modern CPUs and Fortran do not support it natively. https://en.wikipedia.org/wiki/Fixed-point_arithmetic
You can use them in various libraries (even probably Fortran) or languages, but they are not native REAL. They are probably implemented in software, not directly in the CPU and are slower.
I ended up writing a function for this in order to use floating points with the .gt./.lt./.ge./.le./.eq. operators without actually modifying the floating points.
function PreciseInt(arg1, arg2) Result(PreciseInt)
real*8 arg1 !Input variable to be converted
integer*4 arg2 !Input # for desired precision to the right of the decimal
integer*4 PreciseInt !Integer representing the real value with desired precision
PreciseInt = idnint(arg1 * real(10**arg2))
end function

Is Fortran unable to do the addition between 865398.78 and -865398.78? Why the answer is -0.03?

In the code below I am adding together 865398.78 and -865398.78. I expect to get 0, but instead I get -0.03.
Source Code:
program main
real(8) :: x
open(10,file="test.txt")
read(10,*)x
print *,"x=",x
x=x+865398.78
print *,"x+865398.78=",x
end program
Result:
x= -865398.780000000
x+865398.78= -3.000000002793968E-002
Am I wrong with the usage of "read" codes or something else?
The number 865398.78 is represented in single precision in your code. Single precision can handle about 7 significant digits, while your number has 8. You can make it double precision by writing
x=x+865398.78_8
I will make one big assumption in this answer: that real(8) corresponds to double precision.
You are probably assuming that your 865398.78 means the same thing wherever it occurs. In source code that is true: it is a default real literal constant which approximates 865398.78.
When you have
x=x+865398.78
for x double precision, then the default real constant is converted to a double precision value.
However, in the read statement
read(10,*)x
given input "-865398.78" then x takes a double precision approximation to that value.
Your non-zero answer comes from the fact that a default real/single precision approximation converted to a double precision value is not in general, and isn't in this case, the same thing as an initial double precision approximation.
This last fact is explained in more detail in other questions. As is the solution to use x=x+865398.78_8 (or better, don't use 8 as the kind value).

real(8) resolution, data overflow

I have the following code which does not compile using gfortran:
program test_overflow
real(8) a,b
b=0.d0
a=1e39
write(*,*) a*b
end program
The error output from gfortran is
test.f90:4.14:
a=1e39
1
Error: Real constant overflows its kind at (1)
I wonder what is the issue here. As far as I remember, real(8) should give a double precision range of 10 to the power of -100 to +100 (approximately), am I wrong about this?
Use a=1d39 instead of a=1e39.
e denotes single precision.
d denotes double precision.
You may want to refer to the documentation for Double Precision Constants.
The literal
1e139
is a default real. It doesn't matter what is on the left-hand side of the assignment, Fortran always evaluates expressions without the surrounding context. The value 10^139 is too large for the default real in your processor.
real(8) may or may not give you the double precision. Often it does, but it is compiler specific. If you insist on the kind 8, although I won't recommend it, you get floating point literals of the same kind by using the suffix
1e139_8
It is better to place the value 8 to a named constant
integer, parameter :: wp = 8
(though it is better to use other methods, than the magic number 8)
and then you can do
real(wp) :: x
and
1e139_wp
There is an odl method, which distinguishes just default (sometimes called single precision) and double precision reals. In that case you declare your variables as
double precision :: x
and literals as
1d139
Mixing the two methods (real(8) and 1d139) is strange.

Value read from file is stored as a different value in Fortran

I have an input file and the first line contains the following decimal.
0.5053102074297753
I have a Fortran 90 program which reads the file and outputs the value.
read(*,*) answer
write(*,"(F20.16)") answer
This is the output:
0.5053101778030396
Apparently, what is stored is not the same as what is read. The question is, Why?
How is answer declared? If it is a single precision real you can only expect about 6 decimal digits of precision.
Also, values are converted to binary for internal storage and computations. This can cause rounding and other issues, but the difference here is too large for this to be the cause.
To declare answer as double precision, use the following:
integer, parameter :: DRK = selected_real_kind (14)
real (kind=DRK) :: answer
This will guarantee that answer has at least 14 decimal digits. "DRK" can be used throughout your program. Depending on your compiler, you can try asking for even more digits ... it may provide a such a type. Rarely is more than double precision necessary.
What Every Computer Scientist Should Know About Floating-Point Arithmetic.
The default real precision is not enough to store the number with 16 decimal places in the fractional part.