precision of real variable - fortran

I have the following code in FORTRAN 77:
REAL*8 :: dm
dm=1.-1.E-12
write(6,*) 'dm: ', dm
I get: dm: 1
Is this OK? I would like to get dm=0.999999999999

As stated in a comment, you need to specify the precision of the constants. Also, real*8 is obsolete. (Was it always an extension?) Here is a modern way to write this, using the ISO Fortran Environment to obtain a 64-bit real type and using that type both in the declaration and in the constants.
use ISO_FORTRAN_ENV
real (real64) :: dm
dm = 1.0_real64 - 1.0E-12_real64
For more info, see What does `real*8` mean?

Related

How to read in single precision numbers into fortran as double precision?

I have a data file, some of the numbers are really big, like 1E252. How do I read data into Fortran as 1D252. I've tried declaring the variable as double and kind=16, but when its read in as 1E252, it errors.
Unfortunately, the easy solution, to parse the data file and convert all Es to Ds won't work because it also needs to be compatible with python.
Is there something more elegant than this?
program test
Integer :: i
real (kind = 8) :: another_test
Character(len=20) :: char_arr
char_arr = "1.10E+223"
Do i = 1,20
If (char_arr(i:i) == "E") then
char_arr(i:i) = "D"
EndIf
EndDo
Read(char_arr, *) another_test
Write(*,*) another_test
end program test
This isn't a full answer, mainly because I haven't been able to reproduce your premise. (I'm at my home computer, I only have access to gfortran v7.3.0 on Windows 10/cygwin.)
This code compiled perfectly fine and printed the correct value:
program large_float
use iso_fortran_env, only: real64
implicit none
character(len=20) :: strrep
real(kind=real64) :: val
strrep = '1.10E+223'
read (strrep, *) val
print *, val
end program large_float
Now you can see what I do differently to you is this: Instead of the ambiguous kind=8 I use the intrinsic iso_fortran_env to get the correct kind for 64 bit floating values.
I would strongly recommend to use this syntax, as it is compiler independent1 and you will know exactly what you get.
1iso_fotran_env was added I think in Fortran 2003, but I don't know of any Fortran compiler that doesn't recognise it. If you get an error, you can still use the selected_real_kind:
integer, parameter :: real64 = selected_real_kind(R=300)

Is there an equivalent of DEXP for complex*16 arguments in Fortran?

As stated in the question : Is there an equivalent of DEXP for complex*16 arguments in Fortran (90 and later)?
complex*16 isn't Fortran (90 or earlier or later).
If your data type is complex(real64) (or the non-standard complex*16) the standard generic exp() will select the corresponding precision and range.
Where each part (Real and Imaginary) are 64 bits?
If you say COMPLEX(KIND=8) (intel) you get 64 bits for each the real and the imaginary. I'll need to see what gfortran gives...
It may best (I think most portable) to use ISO_C_BINDING and then 'see for yourself' :
PROGRAM ABC
USE ISO_C_BINDING
IMPLICIT NONE
COMPLEX(KIND=C_FLOAT_COMPLEX) :: A
COMPLEX(C_DOUBLE_COMPLEX) :: B
COMPLEX(C_LONG_DOUBLE_COMPLEX) :: C
WRITE(*,*) SIZEOF(A)=',SIZEOF(A)
WRITE(*,*) SIZEOF(B)=',SIZEOF(B)
WRITE(*,*) SIZEOF(C)=',SIZEOF(C)
END PROGRAM ABC
Since Fortran 77 there is very little reason to use DEXP() directly, unless you are passing it as an argument.
In normal expressions (the most common use by far, probably your case) just use the generic EXP() for all exponentiations.
If you have the rare case where DEXP() is passed as an argument to a function then no, there is no equivalent and you have to write a wrapper function which calls the generic EXP().
zexp() and cdexp() in GNU Fortran.
http://gcc.gnu.org/onlinedocs/gfortran/EXP.html

Confusing double precision real in Fortran

Have this burning question on my mind right now: What is the "accepted" way to declare double precision real in modern Fortran? In order from oldest to newest, the story seems to go like this: DOUBLE PRECISION, then REAL(kind=8), then INTEGER, PARAMETER :: dp=kind(1.d0) with REAL(kind=dp)--Metcalf now says dp=kind(0.d0)--and now float32=selected_real_kind(6,37) or float64=selected_real_kind(15,307). So...
How should I be declaring double precision real now?
Is kind redundant in REAL(kind=dp)?
Are there any special flags needed at compile time to invoke double precision real with gfortran or ifort?
Personally I now write
use, intrinsic :: iso_fortran_env
which includes parameters such as int32,real64 which have the obvious meanings, and can be used like this:
real(real64) :: a_64_bit_real_scalar
Note that kind=8 is not guaranteed, by the standard, to deliver an 8-byte kind. The values that kind parameters take are not standardised and do vary from compiler to compiler.
You could, if you want, write statements such as
use, intrinsic :: iso_fortran_env, dp=>real64
...
real(dp) :: a_64_bit_real_scalar
1)How should I be declaring double precision real now?
I personally prefer using the
integer, parameter :: wp = selected_real_kind(15,307)
real(wp) :: var
method for this. But as Mark points out, the iso_fortran_env is another straight-forward method. If you plan on using interfaces to C, you may want to try the ISO_C_BINDING module and use
use iso_c_binding
real(c_double) :: var
And get the double precision you want.
2) Is kind redundant in REAL(kind=dp)?
Yes it is.
3) Are there any special flags needed at compile time to invoke double precision real with gfortran or ifort?
For gfortran you can use the compilation flag -fdefault-real-8. For ifort, you can use the -real-size=64 flag.
The current recommended way of declaring double precision reals in Fortran is:
INTEGER, PARAMETER :: rk = SELECTED_REAL_KIND(15, 307)
REAL(rk) :: x
...

Calling function with configurable real precision

The goal:
Have a function work with configurable working precision.
When I try this:
program vierkantsvergelijking
implicit none
integer, parameter :: dp = kind(0.d0)
integer, parameter :: sp = kind(0.0)
print *, algoritme1(-5771.,2.,dp)
contains
function algoritme1(b,c,wp) result( solution)
integer :: wp ! working precision
real(kind=wp) :: b,c,D
real(kind=wp), dimension(2) :: solution
D = sqrt((b/2)**2 - c)
solution(1) = -b/2 + D
solution(2) = -b/2 - D
end function algoritme1
end program
I get:
Error: Type mismatch in argument 'b' at (1); passed REAL(4) to UNKNOWN
Why is this not working and how can I achieve my goal?
Yes, or rather no, that's not going to work, not no how. The Intel Fortran compiler complains, about this line:
real(kind=wp) :: b,c,D
that
A kind type parameter must be a compile-time constant. [WP]
It makes the same complaint about real(kind=wp), dimension(2) :: solution too. This is a deep-rooted feature of Fortran.
To do what you want you will have to define a generic interface, along these lines
interface algoritme1
procedure :: algoritme1_sp, algoritme1_dp
end interface
and write the code for both those procedures. The compiler can then determine which one is called by the function signature; presumably one would have sp arguments, the other dp arguments.
You might think that this all means that Fortran doesn't do generic procedures, I'll leave that question to the sophists and language nit-pickers. It would be worth your while to search around for generic programming in Fortran; even here on SO there are tricks and tips on how to better avoid writing multiple implementations of what the programmer believes (at odds with the compiler) to be the 'same' code.

How to automatically convert a Fortran90 program whose variables are in double precision into real?

everyone
I have a Fortran90 program, and the variables are in double precision or complex*16, now I have to write another program whose variables are in real or complex, and all other things are the same as the original program.
The straightforward way is to rewrite every declaration, but I'm wondering if there are other simpler ways to achieve this, I'm using gfortran as the compiler.
Thanks
Probably the cleanest (althoug not the easiest) way would be to rewrite your program to have adjustable precision for the variables:
program test
implicit none
integer, parameter :: rp = kind(1.0d0)
real(rp) :: myreal
complex(rp) :: mycomplex
By setting the parameter rp (real precision) to kind(1.0) instead of kind(1.0d0) you can switch from double to single. Alternatively, with fortran 2003 compatible compilers you can also use the names real64 and real32 after invoking the iso_fortan_env module. (UPDATE: it needs a fortran 2008 compatible compiler, not fortran 2003, see the comment of IanH).