Double precision in Fortran for trignometric functions [duplicate] - fortran

This question already has answers here:
Result of GAMMA underflows its kind
(2 answers)
Is There a Better Double-Precision Assignment in Fortran 90?
(2 answers)
Best practice when working with double precision magic numbers
(1 answer)
Closed 1 year ago.
I am using the following code to calculate cos for pi/2 in Fortran.
program precision_Fortran
IMPLICIT NONE
!!integer, parameter :: dp = kind(1.0d0) !!Gives same result as line below
integer, parameter :: dp = selected_real_kind(15, 307)
Real(dp), parameter:: pi=4.0*atan(1.0)
Real(dp) :: angle
angle = cos(pi/2.0)
write(*,*)'pi = ', pi
write(*,*)'angle = ', angle
end program precision_Fortran
I compiled using gfortran and ftn95. From both, the output is
pi = 3.1415927410125732
angle = -4.3711390001862412E-008
How do I get a better precision for angle here? For instance in C++ I see it in order of E-18, for all declaration using double.
Please let me know if more information is needed to explain it better.
Extra : The main code I am using, with physical equations having trigonometric terms, is having precision issues, and am not entirely sure, but am suspecting it's because of this. So, want to check if above could be improved somehow. Not expert with Fortran so struggling to figure this out.

Related

Converting Integer to Double Precision in fortran [duplicate]

This question already has an answer here:
Real value after assignment different from the real expression
(1 answer)
Closed 1 year ago.
I am seeing a strange problem, when I convert a large int to float, the values are not the same,
Here's a test program that replicates the problem
program test
integer a
a = 135000011
b = dble(a)
write(*,*) a, b
end
This prints
135000011 135000012
What is happening? How can I circumvent this?
I have found the error and how to fix this?
Since the value of b is not declared, fortran implicitly assumes its to be real, so to mitigate and correct the issue b should be declared as double precision
full program here
program test
integer a
double precision b
a = 135000011
b = dble(a)
write(*,*) a, b
end
This prints
135000011 135000011.00000

Floating point math accuracy, c++ vs fortran [duplicate]

This question already has answers here:
Different precision in C++ and Fortran
(2 answers)
Closed 2 years ago.
I have tried to implement a recursive function, in both C++ and Fortran, which calculates the value of the n'th Legendre polynomial, at x. In Fortran I have
recursive function legendre(n, x) result(p)
integer, intent(in) :: n
real(8), intent(in) :: x
real(8) :: p
if(n == 0) then
p = 1.0
else if (n == 1) then
p = x
else
p = (2.0*real(n,dp)-1.0)*x*legendre(n-1,x)-(real(n,dp)-1.0)*legendre(n-2,x)
p = p / real(n,dp)
end if
end function legendre
and then in C++ I have
double legendre(int n, double x) {
double p;
if(n == 0) return 1.0;
else if(n == 1) return x;
else {
p = (2.0*(double)n - 1.0)*x*legendre(n-1,x)-((double)n - 1.0)*legendre(n-2,x);
p /= (double)n;
return p;
}
}
These two functions seem to be exactly the same to me, both using double precission, but the result from the Fortran function is substantial different from the C++ result. For example,
legendre(7,-0.2345) = 0.28876207107499049178814404296875 according to WolframAlpha. The two codes above, when compiled with no optimizations produce
Fortran : 0.28876206852410113
C++ : 0.28876207107499052285
I know that the answers should not be the same due to floating point arithmetic, but the difference in value here for double precision seems somewhat large to me. What is the reason that the Fortran value is so far off from the other two ?
Although the variables in your FORTRAN function are defined as double-precision (8 bytes), the constants you have specified are default (single-precision, 4-byte) values.
According to this discussion, that means the arithmetic is performed to single-precision accuracy:
Even if the variable that you are assigning the result to is defined
to be DP, the Fortran standard requires that the arithmetic on the
constants be performed using SP. That is because you are using default
real constants, since you do not have any kind type parameter at the
end of the constants. By rule, default real is SP.
And, further on in the same discussion:
...Starting with Fortran 90, published in June 1991, this practice of
"promoting" SP constants to DP is prohibited.
So, in order to force double-precision maths, specify the constants as DP: instead of, for example, 1.0, specify 1.0D0 (and so forth for the others).
Thanks to Adrian's response, I was able to fix the problem. Nothing was wrong with the code in the Fortran function, the issue was the value of x which I was passing to it. Even though in the main program, I had defined x as real(8) and latter assigned it the value with a simple
x = -0.2345
which I thought should be double precision. It should actually be
x = -0.2345_dp
This results in the two functions having the same answer. I believe it is likely due to the reason that Adrian pointed out.

Fortran: Passing in a hardcoded value to a function accepting a real(16) doesn't work. How can I fix this? [duplicate]

This question already has answers here:
Using scientific notation and underscore kind specifier at the same time for real literals in Fortran
(1 answer)
Numerical Precision in Fortran 95:
(2 answers)
Closed 4 years ago.
Relevant code:
Parts(3, indexcount) = push(pi/2, 0.0d0,0.0d0,0.0d0, Parts(1, indexcount),Parts(2, indexcount), 0.01d0, 20000.0d0)
real(16) function push(phaseinit, yinit, pxinit, pyinit, a0, R, dt, Time_total)
implicit none
real(16) :: phaseinit, yinit, pxinit, pyinit, gammamax, I0, R_star, k_ratio, Vph
etc...
When compiling:
Error: Type mismatch in argument 'yinit' at (1); passed REAL(8) to REAL(16)
I tried to find online resources detailing how exactly to properly initialize a numerical value for a real(16), but I couldn't find anything. This seemingly allows me to initialize real(8), but I don't know what the corresponding procedure is for real(16).

Function type does not match the function definition

I am new to Fortran, writing some practice code with a function that returns Farenheit from Celsius
program Console1
implicit none
real, parameter :: ikind = selected_real_kind(p=15)
real (kind = ikind):: c,f,o,faren
print *, "enter a temperature in degrees celsius"
read *, c
write(*,10) "farenheit =", faren(c)
10 format(a,f10.8)
end program Console1
function faren(c)
real, parameter :: ikind = selected_real_kind(p=15)
real (kind = ikind):: c,f
faren = (9/5)*c + 32
end function faren
I get an error #7977 : The type of the function reference does not match the type of the function definition.
So with that if i change function faren(c) to real function faren(c)
I get the same error, but the types are the same?
Am i missing something? Do I have to define the function in the main program?
There are several issues in addition to the structural/code arrangement ones already noted.
First, KIND is an integer, so you want to change
real, parameter :: ikind = selected_real_kind(p=15)
to
integer, parameter :: ikind = selected_real_kind(p=15)
Ideally, you want to define that in only one place (i.e. in a module) and reference it from both your main program and the function, but the code should be fine as it is for test purposes.
A second issue that often trips up newcomers to Fortran (and Python2) is that real numbers and integers are distinct types and are not generally interchangeable.
faren = (9/5)*c + 32
simplifies to
faren = (1)*c + 32
because integer division has an integer result; 9/5 = 1
Fortran is picky about numerical values (that's sort of the whole point of the language) so what you probably want is:
faren = (9.0 / 5.0) * c + 32.0
Or more precisely, if faren is defined with a specific precision of ikind,
faren = (real(9.0,ikind) / real_(5.0,ikind)) * c + real(32.0,ikind)
or
faren = (9.0_ikind / 5.0_ikind) * c + 32.0_ikind
This syntax tends to make people's heads explode. Welcome to modern fortran ;)
The last issue deals with the horrors of Fortran I/O. From a design standpoint, you need to know what results the user expects and make sure the output format can display them. The legitimate range of input values for c is -273.15 (give or take) to some upper bound which relies on the use case for the code. If you're dealing with cooking temperatures, you probably won't exceed 400.0; if you're doing fusion research, you could be going much higher. Are 8 figures past the decimal useful or believable? In this case, we're just testing the code so we may not need a lot of precision in the output; you'll want to change the output format to something like:
10 format(a,es10.2)
or
10 format(a,g16.8)
You need to ensure the total field width (the number before the dot) can contain the decimal part (the number after the dot) along with the integer part of the number, plus the space needed to show sign and exponent. For scientific notation, four characters are eaten by mantissa sign, decimal point, 'E' and exponent sign. It may be safer just starting out to use an output format of *; it's frustrating to fight with numerics and formatting simultaneously.
That is a good effort and simple start to work through the nuance, so a good question.
Personally I would use reals for the math, rather the 9/5, and use a module. In this example you could pass in a real or a double to C2Faren and the interface/procedure will sort out whether to use the real or the double version. Then you have a few options in case you want different precision.
You could also use the ISO_C_BINDING if you do mixed language...
MODULE MyTEMPS
PRIVATE
DOUBLE PRECISION, PARAMETER :: C2F_ScaleFact = 1.8D0
DOUBLE PRECISION, PARAMETER :: F2C_ScaleFact = /(1.0D0 / 1.8D0)/
DOUBLE PRECISION, PARAMETER :: F2C_Offset = 32.0D0
PUBLIC Faren2C
INTERFACE C2Faren
MODULE PROCEDURE C2Faren_Real, C2Faren_DBL
END INTERFACE
CONTAINS
!========= REAL VERISON =========
REAL FUNCTION C2Faren_Real(c)
IMPLICIT NONE
real, INTENT(IN ) :: c
C2Faren_Real = ( C*F2C_ScaleFact ) + F2C_Offset
RETURN
END FUNCTION C2Faren_Real
!========= DOUBLE VERSION =========
DOUBLE PRECISION FUNCTION C2Faren_DBL(c)
IMPLICIT NONE
DOUBLE PRECISION , INTENT(IN ) :: c
C2Faren_DBL = ( C*F2C_ScaleFact ) + F2C_Offset
RETURN
END FUNCTION C2Faren_DBL
!========= REAL VERSION (Faren to Centigrade) =========
REAL FUNCTION faren2C(Faren)
IMPLICIT NONE
REAL, INTENT(IN ) :: Faren
faren2C = (faren - F2C_Offset) / F2C_ScaleFact
RETURN
END FUNCTION faren2C
END MODULE MyTEMPS
Then your program uses the module via USE n the second line...
program Console1
USE MyTEMPS !<== Here
implicit none
real :: c, f
DOUBLE PRECISION :: Dc, Df ! No way to get Df to C or DC in the module (yet)!
print *, "enter a temperature in degrees celsius"
read *, c
write(*,10) "farenheit =", C2faren(c)
10 format(a,f10.6)
Dc = C
write(*,12) "farenheit =", C2faren(Dc)
12 format("DBL:",A,f10.6)
F = Dc
write(*,14) "Centigrade =", faren2C(F)
14 format("DBL:",A,f10.6)
end program Console1
So/and the main advantage of the module is when you end up wanting to use this stuff in a variety of programs and test and sort out the module once... Usually people put this sort of stuff (lots of modules) in a library, when the module(s) have lot of functions.
You could also put just the real, parameter :: ikind = selected_real_kind(p=15) into a module and use that in both the program and the function and you would be there. You were real close, and it mostly a matter of style and utility.
For Intel Fortran you can use REAL(KIND=4) and REAL(KIND=8)... Which I do, but that is not portable to gfortran, so it is probably a better habit to use the ISO_C_BINDING or just use REAL and DOUBLE PRECISION.
Modules are great but if you have a very simple code another way to work is to put the subroutines and functions in your main program. The trick is to put them after the word contains:
program xxx
stuff
contains
subroutine yyy
function zzz
end program xxx
In this way the functions can see into the contents of the main program so you don't have to re-declare your parameters and you are likely to get more meaningful error messages.
Since you are new I have a great resource I learned a lot from to share:
http://www.uv.es/dogarcar/man/IntrFortran90.pdf

Result of GAMMA underflows its kind

I would like to calculate gamma(-170.1) using the program below:
program arithmetic
! program to do a calculation
real(8) :: x
x = GAMMA(-170.1)
print *, x
end program
but I get the error:
test.f95:4.10:
x = GAMMA(-170.1)
1
Error: Result of GAMMA underflows its kind at (1)
when I compile with gfortran. According to Maple gamma(-170.1) = 5.191963205*10^(-172) which I think should be within the range of the exponent of the variable x as I've defined it.
The below modification of your program should work. Remember that in Fortran the RHS is evaluated before assigning to the LHS, and that floating point literals are of default kind, that is single precision. Thus, making the argument to GAMMA double precision the compiler chooses the double precision GAMMA.
program arithmetic
! program to do a calculation
integer, parameter :: dp = kind(1.0d0)
real(dp) :: x
x = GAMMA(-170.1_dp)
print *, x
end program
-170.0 may be treated as a float. If so, changing it to a double should resolve the issue.