Quadruple precision for abs and sign - fortran

I am trying to change the precision of the abs and sign with gfortran (gcc version 5.3.1 and x86_64-linux-gnu).
I know I can change to dabs and dsign to have double precision but what about quad precision, is it possible?
For sqrt(x) for instance, I simply change to x**(0.5q0) with the arg defined as a real(16). I read that some compilers do not have the intrinsic routines implemented in quad or extended precision, see e.g. here.

If gfortran has been compiled with support for libquadmath (on Linux it typically is), then it supports this right away. All you need to do is declare your variables and literals quadruple precision, and use the generic versions abs and sign.
However, using real(16) is not portable (and outdated). Better use something standardized such as selected_real_kind(p=30).
Here is a short example to illustrate this:
program test
integer, parameter :: qp = selected_real_kind(p=30)
real(qp) :: a
a = -2._qp
print *, abs(a)
print *, sqrt(-a)
end program
The output is:
2.00000000000000000000000000000000000
1.41421356237309504880168872420969818
Compare this to the result of Wolfram alpha:
1.414213562373095048801688724209698078569671875376948073176

Or...
https://software.intel.com/en-us/node/525335
PROGRAM!SUBROUTINE!FUNCTION
USE ISO_C_BINDING
IMPLICIT NONE
REAL(KIND=C_LONG_DOUBLE) :: A
!or maybe...
REAL(C_LONG_DOUBLE) :: B
...

Related

ISO_FORTRAN_ENV or -fdefault-real-8 to promote reals to double precision

I've always been using the -fdefault-real-8 option of gfortran to automatically promote every single REAL declared anywhere in the program to double precision, along with any constant, e.g. 1.23. If I ever wanted to switch back to single precision, I only had to remove that option and recompile, without changing a single character in the source code.
At a point I started using ISO_FORTRAN_ENV module, since it allows me to use constants like INPUT|OUTPUT|ERROR_UNIT, as well as IOSTAT_END and IOSTAT_EOR and others (which seemed to be a good and easy move in the direction of portability, am I wrong?). From then on, I've been seeing and ignoring the following warning
Warning: Use of the NUMERIC_STORAGE_SIZE named constant from intrinsic module ISO_FORTRAN_ENV at (1) is incompatible with option -fdefault-real-8
since such incompatibility seems to have no effect so far.
Now I'd like to get rid of this warning if it is possible and worth it.
If I correctly understood, to avoid this warning I should give up on -fdefault-real-8 option and change every REAL to REAL(real64) and/or to REAL(dp) (provided that, in the latter case, the statement USE, INTRINSIC :: ISO_FORTRAN_ENV, dp => real64 is put in that unit), which is not a difficult task for sed or vim.
Nevertheless, it seems to me that this change wouldn't be the same as using -fdefault-real-8 option, since all constants would stay single precision as long as I don't add d0 to them.
Assumed the -fdefault-real-8 option is removed and ISO_FORTRAN_ENV is used anywhere, is there any way to make any constant across the program behave as each had d0 suffix?
Whether or not this is possible, have I correctly extrapolated that I can put the following lines in a single module which is used by all others program units, each of which can then use dp as kind type parameter?
USE, INTRINSIC :: ISO_FORTRAN_ENV
INTEGER, PARAMETER :: dp = real64
I would prefer this way since I could switch to real32 or real128 or whatever by changing only that line.
If you just want to silence the warning and you do not care about the implications -fdefault-real-8 has on storage association and some Fortran standard requirements, just do not import NUMERIC_STORAGE_SIZE from the module. For example,
USE, INTRINSIC :: ISO_FORTRAN_ENV, only: INPUT_UNIT,OUTPUT_UNIT,ERROR_UNIT
Assumed the -fdefault-real-8 option is removed and ISO_FORTRAN_ENV is used anywhere, is there any way to make any constant across the program behave as each had d0 suffix?
No.
By the way, d0 is exactly the same as double precision, so that doesn't fixate much either, since the meaning of double precision is allowed to vary as much as real.
Whether or not this is possible, have I correctly extrapolated that I can put the following lines in a single module which is used by all others program units, each of which can then use dp as kind type parameter?
Yes. That is a common practice.

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

fortran 64 bit hex BOZ

in C++ this is accepted:
uint64_t mask = 0x7FC0000FF80001FFLL;
but in fortran
integer(kind=8), parameter :: mask = Z'7FC0000FF80001FF'
does not work with gfortan.
I think both of them are 64bit values? or not?
gfortran complains:
arithmetic overflow from converting INTEGER(16) to INTEGER(8)
EDIT:
So, sorry for the confusion, here is some more extended problem description.
I will do some bit shifting in Fortran and have some sample code in c++.
There in the sample c++ code the masks are defines like:
typedef uint64_t mask;
static const mask dilate_2 = (mask)0x7FC0000FF80001FFLL ;
static const mask dilate_1 = (mask)0x01C0E070381C0E07LL ;
static const mask dilate_0 = (mask)0x9249249249249249LL ;
From my poor c++ understanding, I think that the hex values are 64bit
integer values (they have LL in the ending).
Now in Fortran my problem first was, that the definition with
integer(kind=8), parameter ...
did not work, as Vladimir said, because
integer(kind=8), ...
might be no 64bit integer.
Than I tested Alexanders solution, which works for the first and the
second (dilate_2, dilate_1) constant.
Also Vladimirs solution works for these two.
Now for dilate_0 none of these solutions work. I would suppose that Vladimirs solution will cast 0x9249249249249249LL (what is actually
a greater integer than allowed in INT64) into a INT64
if I do:
integer(INT64), parameter :: dilate_0 = int(Z'9249249249249249', &
kind=kind(dilate_0)
But this also don't work and gfortran give me an error:
Error: Arithmetic overflow converting INTEGER(16) to INTEGER(8) at (1).
So my actual question is how to implement this constant in Fortran?
As Vladimir posted in his comment integer(kind=8) is not portable (and not of kind 16 as the compiler complains).
As a remedy I suggest to use the intrinsic module ISO_Fortran_env (Fortran 2003) which has many predefined constants for the compiler used. Using INT64 from this module solves your problem and results in portable code:
program test
use,intrinsic :: ISO_Fortran_env, only: INT64
integer(INT64), parameter :: mask = Z'7FC0000FF80001FF'
print *,mask
end program
Z'9249249249249249' is not representable as a an INT64 (which is equivalent to an INTEGER(kind=8) in gfortran) because
BOZ constants are signed numbers (the same as every other integer constant in Fortran)
This number is larger than 2**63-1, the largest representable number for an INT64
Gfortran therefore selects the smallest integer type which fits, which is INTEGER(KIND=16).
We then have parameter staement where an INTEGER(KIND=8) parameter should be assigned a value outside its range. This is what the compiler complains about. It would complain the same way about
INTEGER(KIND=4), PARAMETER :: N = 37094947285
If you want to get around this, you can use the -fno-range-check option to gfortran. Information about -fno-range-check is already included in the gfortran error message (the part you didn't show).
I would do this to stay standard conforming
integer(whatever), parameter :: mask = int(Z'7FC0000FF80001FF', &
kind=kind(mask))
where whatever is some kind constant of the required value. It could be int64.
The above will not work if the constant corresponds to a negative number. One then has to make a trick like:
integer(int32), parameter :: mask = transfer(int(Z'A0000000',int64),1_int32)
or
integer(int32), parameter :: mask = transfer(Z'A0000000',1_int32)
but I am not sure whether the last one is strictly standard conforming.

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
...

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).