I have a fortran code being compiled in gfortran (several thousand lines so I'll try to post the important lines) that gives me:
nrev(isat)=dint((t_ref-t_in)/zper)+1
1
Warning:Possible change of value in conversion from REAL(8) to INTEGER(4) at (1)
They are initialized as:
integer*4 nrev(nmaxsat)
integer*4 isat
real*8 t_ref
real*8 t_in
real*8 zper
Any ideas on how to fix this?
Thanks!
It's an excellent idea to get rid of all warnings, even minor ones -- even if only so that when you do get more significant issues, you see them rather than having the output swamped by little things.
In this case, the warning message is fairly clear; you're assigning a double to an integer. The dint intrinsic truncates, but it doesn't convert types; so you're assigning a double precision value that's had it's value truncated to an integer. You could rightly note that the intrinsic is confusingly named, but...
If you want to do the conversion as well as the truncation, idint actually converts to an integer.
So for instance this program
program foo
integer :: nrev
double precision :: t_ref
t_ref = 1.
nrev = dint(t_ref)
end program foo
creates the same warning:
$ gfortran -o foo foo.f90 -Wall -std=f95
foo.f90:8.11:
nrev = dint(t_ref)
1
Warning: Possible change of value in conversion from REAL(8) to INTEGER(4) at (1)
But this one is fine:
program foo
integer :: nrev
double precision :: t_ref
t_ref = 1.
nrev = idint(t_ref)
end program foo
as we see:
$ gfortran -o foo foo.f90 -Wall -std=f95
$
Related
I'm modernizing some old Fortran code and I cannot get rid of an equivalence statement somewhere (long story short: it's mixed use is so convoluted it'd take too much work to convert everything).
I need the length of the EQUIVALENCEd arrays to depend on some input, like the following code:
program test_equivalence
implicit none
type :: t1
integer :: len = 10
end type t1
type(t1) :: o1
call eqv_int(o1%len)
call eqv(o1)
return
contains
subroutine eqv_int(len)
integer, intent(in) :: len
integer :: iwork(len*2)
real(8) :: rwork(len)
equivalence(iwork,rwork)
print *, 'LEN = ',len
print *, 'SIZE(IWORK) = ',size(iwork)
print *, 'SIZE(RWORK) = ',size(rwork)
end subroutine eqv_int
subroutine eqv(o1)
type(t1), intent(in) :: o1
integer :: iwork(o1%len*2)
real(8) :: rwork(o1%len)
equivalence(iwork,rwork)
print *, 'LEN = ',o1%len
print *, 'SIZE(IWORK) = ',size(iwork)
print *, 'SIZE(RWORK) = ',size(rwork)
end subroutine eqv
end program test_equivalence
This program will create 0-length arrays with gfortran 9.2.0. This is the output:
LEN = 10
SIZE(IWORK) = 0
SIZE(RWORK) = 0
LEN = 10
SIZE(IWORK) = 0
SIZE(RWORK) = 0
The same code will return Array 'rwork' at (1) with non-constant bounds cannot be an EQUIVALENCE object when compiled with gfortran 5.3.0, the warning disappears since gfortran 6.2.0, but the size of the arrays is always 0. So maybe compiler bug?
The source code is indeed not a valid Fortran program. To be specific, it violates the numbered constraint C8106 of Fortran 2018:
An equivalence-object shall not be a designator with a base object that is .. an automatic data object ..
Being a numbered constraint, the compiler must be capable of detecting this violation. If hasn't such a capability this is a deficiency in the compiler (a bug). Being "capable" doesn't mean doing so by default, so please look carefully to see whether there are options which do lead to this detection. Someone familiar with the internals of GCC can give further detail here.
As the source isn't a valid Fortran program, the compiler is allowed to consider the arrays of size zero if it has skipped the violation detection.
I have a large Fortran code in which most of the real variable is declared to have double precision by using
double precision
real*8
And I wonder if it is possible to force the compiler to use single-precision for those variables.
This depends on the compiler that you use. GNU Fortran supports the option -freal-8-real-4, which transforms all 8-byte reals to 4-byte ones. For instance, the following program
program p
use iso_fortran_env, only: real64
real(kind=real64) :: x
double precision :: y
real*8 :: z
print *, sizeof(x), sizeof(y), sizeof(z)
end program p
prints
8 8 8
but when compiled with -freal-8-real-4, the outputs is
4 4 4
Other compilers may have different options (or none of such kind).
I was doing an svd decomposition of a square matrix A, with A=U S Vdag, and in the fortran code, the line reads
lwork = -1
call zgesvd( 'A', 'A', A%d, A%d, A%m, A%d, S, U%m, U%d, Vdag%m, Vdag%d,work, lwork, rwork, info )
lwork = int(work(1));deallocate(work); allocate(work(lwork))
call zgesvd( 'A', 'A', A%d, A%d, A%m, A%d, S, U%m, U%d, Vdag%m, Vdag%d,work, lwork, rwork, info )
When I compiled with gfortran, it went through with no error or warning. However when I run the program, it shows error with message:
" ** On entry to ZGESVD parameter number 11 had an illegal value "
I could not figure out what went wrong.
For reference, the definitions of the parameters:
type cmatrix
integer(4) d
complex(8), allocatable :: m(:,:)
end type
type (cmatrix) A,U,Vdag
allocate(A%m(dim,dim),U%m(dim,dim),Vdag%m(dim,dim))
A%d = dim; U%m = dim; Vdag%d = dim
real(8) S(dim)
Thanks in advance!
Xiaoyu
p.s. It should be mentioned that such a program runs smoothly when compiled with ifort, but gfortran gives an runtime error as shown above
--- Problem solved!
It seems that the issue lies in how ifortran and gfortran allocates memory. I defined in the code USV type which:
type USV
integer is_alloc
type (cmatrix) U,V
real(8), allocatable :: S(:)
end USV
When initializing by
type(USV) Test_usv(:)
allocate(Test_usv(3)),
the value of is_alloc is 0 using intel fortran compiler, while arbitrary number for gfortran. I need to use this value as a criterion for allocating U V matrices:
if (is_alloc.eq.0) then
allocate(U%m(dim,dim))
end if
The fundamental problem is not a difference between ifort and gfortran. Your approach to the initialization of the variables isn't valid Fortran. Unless you initialize a variable with a declaration, assignment statement, etc., its value is undefined. One way to fix this would be to add a default initialization to the type definition:
type USV
integer is_alloc = 0
type (cmatrix) U,V
real(8), allocatable :: S(:)
end USV
Another approach would be to not track the allocation status yourself and to rely upon the intrinsic function that Fortran provides for this purpose:
if (.NOT. allocated (U%m) ) allocate(U%m(dim,dim))
P.S. Best practice is not to rely upon specific numeric values for kinds. The kind values are arbitrary and are not necessarily the number of bytes of the type. Some compilers use the number of bytes, others don't. One method to specify the number of bytes and to have portable code is to use types provided by the ISO Fortran environment:
use, intrinsic :: ISO_FORTRAN_ENV
integer(int32) :: d
real(real64), allocatable :: S(:)
The types are named after the number of bits. A list of the available types is in the "Intrinsic Modules" chapter of the gfortran manual.
I have an issue that hopefully would not affect my numerical calculations in Fortran 90.
The thing is that I have an array declared as
Real(r8), Allocatable :: matKBody(:)
By my hand, initializing such array I done it like
allocate(matKBody(1:Nk), STAT=ierr)
If (ierr /= 0) Stop
matKBody(:) = ( 0.0_r8, 0.0_r8)
which is a way to initialize a complex array. I noticed this error but I was surprised
that it doesn't matter if I initialize such array in this way or using the 'correct' statement:
matKBody(:) = 0.0_r8
...
... ! Do some stuff with the array
...
Deallocate( matKBody, STAT=ierr )
If (ierr /= 0) Stop
If I print both arrays they give me the correct initialization , i.e. both initialize
real numbers.
Why fortran (or the compiler) is not aware of such kind of things?
(I used ifort for compilation).
Full example:
program test_convert
use, intrinsic :: ISO_FORTRAN_ENV
Real(real64), Allocatable :: matKBody(:)
allocate (matKBody (1:10) )
matKBody(:) = ( 0.0_real64, 0.0_real64)
end program test_convert
Compiled with gfortran with options: -O2 -fimplicit-none -Wall -Wline-truncation -Wcharacter-truncation -Wsurprising -Waliasing -Wimplicit-interface -Wunused-parameter -fcheck=all -std=f2008 -pedantic -fbacktrace
Output from gfortran:
matKBody(:) = ( 0.0_real64, 0.0_real64)
1
Warning: Possible change of value in conversion from COMPLEX(8) to REAL(8) at (1)
So some compiler are "aware" of such things and will tell you about them if you request it to.
As discussed in the comments, Fortran provides automatic conversion between types upon assignment. So this is not an error, but gfortran at least will provide a warning so that the programmer can check whether they intended the conversion. You can suppress the warning and state your intent to cause a conversion via:
matKBody(:) = real ( ( 0.0_real64, 0.0_real64), real64 )
(This is just an example, since writing = 0.0_real64 is so much simpler for this particular assignment.)
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.