Implicit conversion integer <--> logical in Fortran if statement - fortran

I have some legacy Fortran code which I was asked to analyze and translate to a modern language. I don't know which compiler was used in the past to compile the code, so for now, I'm trying to compile it with gfortran. The code contains a statement like this was causes gfortran to complain:
program test
implicit none
integer*4 :: var
var=.true.
if(var) then
write(*,*) "Hi"
endif
end program test
Compiling this with gfortran gives the following error:
test.f:6:9:
if(var) then
1
Error: IF clause at (1) requires a scalar LOGICAL expression
(In addition, it gives a warning about the conversion done in var=.true.).
I'm not sure which which compiler the code was compiled, but apparently the code should compile as it is. Is there a way to tell gfortran to accept this conversion?
According to the docs, no implicit conversion is done within if-statements though: https://gcc.gnu.org/onlinedocs/gfortran/Implicitly-convert-LOGICAL-and-INTEGER-values.html

This is not possible in GFortran. The manual states:
However, there is no implicit conversion of INTEGER values in
if-statements, nor of LOGICAL or INTEGER values in I/O operations.
You are only able to perform implicit conversions in assignments like your
integer :: var
var = .true.
but even there you must be very careful. It is not standard conforming and the value var will differ between compilers. Intel used to use -1 (all bits set to 1), unless -standard-semantics was chosen, for .true., but gfortran uses +1 as in the C language. New versions of Intel Fortran changes the default. The other direction is even trickier, there might be values which are neither .true. nor .false..

Related

gfortran compiler error calling recursive log_gamma function

I uploaded a 2F1 hypergeometric function but it turns out that it does not compile on my computer. It is from this article. I use
GNU Fortran (Built by Jeroen for the R-project) 8.3.0
shiped with RTools 4.0. Can you figure out why it does not compile and how this could be solved? In the code below, I just kept the one line that generates the error. The error is given next.
MODULE HYP_2F1_MODULE
!--------------------------------------------------------------------
IMPLICIT NONE
INTEGER, PARAMETER :: PR=KIND(1.0D0)
REAL(PR) :: ONE=1.0D0
CONTAINS
!
END MODULE HYP_2F1_MODULE
!
!----------------------------------------------------------------------
RECURSIVE FUNCTION LOG_GAMMA(Z) RESULT(RES)
USE HYP_2F1_MODULE
IMPLICIT NONE
COMPLEX(PR),INTENT(IN) :: Z
COMPLEX(PR) :: RES
!
RES = LOG_GAMMA( ONE -z);
!
END FUNCTION LOG_GAMMA
Here is the error message
testZ.f90:18:22:
RES = LOG_GAMMA( ONE - Z);
1
Error: 'x' argument of 'log_gamma' intrinsic at (1) must be REAL
There is a Fortran 2008+ intrinsic of the same name
16.9.119 LOG_GAMMA (X)
1 Description. Logarithm of the absolute value of the gamma function.
2 Class. Elemental function.
3 Argument. X
shall be of type real. Its value shall not be a negative integer or
zero.
There is some sort of clash I do not completely understand here. The obvious workaround is to rename your function. What I can say is that without the result clause (which you need because of the recursive attribute) the intrinsic would be shadowed. It might be a compiler bug.
Also consider, whether the intrinsic of the same name couldn't also do what you need from your function.
In addition to #VladimirF's suggestions, you can add the compiler flag -std=f95 to enforce compliance with the Fortran 95 standard. Provided you haven't added -fall-intrinsics this will disable all intrinsics from standards after Fortran 95, and should make the legacy code work.

Fortran compiler options to warn about assignment of single precision constant to a double precision variable [duplicate]

I am tasked with changing the precision of parts of an HPC application, bearing in mind that it makes heavy reliance on auto-vectorisation. It is therefore useful for the compiler to inform me when conversions of any type of floating point conversion occurs (as this could have a serious performance impact).
The -Wconversion flag sounds like it should suit my needs:
-Wconversion
Warn about implicit conversions between different types.
https://gcc.gnu.org/onlinedocs/gcc-4.1.0/gfortran/Warning-Options.html
However, in practice, gfortran 5.2.0 only appears to report floating point demotions, e.g. REAL(8) to REAL(4).
GCC has the -Wdouble-promotion flag - exactly what I need, but not available for gfortran. (https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html)
I am developing with gfortran, but ifort is available to me. However, I can't find any similar arguments for -warn (https://software.intel.com/en-us/node/525184).
How can I get either of these compilers to emit a warning when implicitly promoting a REAL?
You refer to using gfortran 5.2.0, so let's look at the documentation for that version rather than 4.1.0. This has two relevant flags for what you consider:
-Wconversion
Warn about implicit conversions that are likely to change the
value of the expression after conversion. Implied by -Wall.
-Wconversion-extra
Warn about implicit conversions between different types and
kinds. This option does not imply -Wconversion.
If I use this latter flag with the following program
use, intrinsic :: iso_fortran_env, only : real32, real64
real(real64) x
x = 1._real32
end
I get exactly (albeit using gfortran 4.8.1) a warning message requested in the question title
Warning: Conversion from REAL(4) to REAL(8) at (1)
whereas with just -Wconversion I get nothing. If I change the program slightly, however, so that the changing of representable values kicks in, I get (different) warnings with each.
ifort, on the other hand (up to 19.0.5), appears to have no comparable warning.

Error with parentheses when assigning to complex variable

I have the following set of commands in my Fortran code:
COMPLEX*16, DIMENSION(4,1) :: INSTATE_BASISSTATES
INSTATE_BASISSTATES(:,:) = (0.0D0,0.0D0)
INSTATE_BASISSTATES(1,1) = ((1.0D0/SQRT(2)),0.0D0)
INSTATE_BASISSTATES(3,1) = ((1.0D0/SQRT(2)),0.0D0)
When I run/compile the program using gfortran on cygwin, I get the error
INSTATE_BASISSTATES(1,1) = (1.0D0/DREAL(SQRT(2.0D0)),0.0D0)
1
Error: Expected a right parenthesis in expression at (1)
INSTATE_BASISSTATES(3,1) = (1.0D0/DREAL(SQRT(2.0D0)),0.0D0)
1
Error: Expected a right parenthesis in expression at (1)
What could be the issue? Aren't my brackets correct?
On the right hand side of the assignment statement you are trying to use a complex literal constant. However,
(1.0D0/DREAL(SQRT(2.0D0)),0.0D0)
isn't a valid form for such a constant.
For a complex literal, the real and imaginary components must be either named constants or literal constants. 1.0D0/DREAL(SQRT(2.0D0)) is neither of those things. For the line where you had no complaint, both components of (0.0D0,0.0D0) are literal constants.
As in this other answer you could make a named constant with the value wanted and use that. Alternatively, as you are just doing a boring assignment (which doesn't have various restrictions which apply to initialization, etc.,) you can use the cmplx intrinsic to return a complex value
INSTATE_BASISSTATES(1,1) = CMPLX(1.0D0/DREAL(SQRT(2.0D0)),0.0D0)
Here the real and imaginary components don't need to be constants. You could even note that
INSTATE_BASISSTATES(1,1) = CMPLX(1.0D0/DREAL(SQRT(2.0D0)))
works just as well: if the imaginary component value isn't provided, the returned complex has imaginary component zero.
There is a slight complication, though. cmplx by default returns a complex number with kind of the default real. To return something matching complex*16 (which isn't standard Fortran, but let's assume it corresponds to double precision) you'll need CMPLX(..., [...], KIND=KIND(0d0)) (or KIND=KIND(INSTATE_BASISSTATES))
As a side note, as Vladimir F comments dreal isn't standard Fortran. You could use dble, or real with a suitable kind number. But we can also see that sqrt(2d0) already returns a double precision real, so even those are redundant: 1/sqrt(2d0) has the same (mathematical) result as the original more cumbersome expression. As do 2d0**(-0.5) and sqrt(2d0)/2.
You could even replace the right hand side with
SQRT((5d-1,0))
as we see that sqrt also accepts a complex argument (in this case a complex literal constant). This form also avoids the awkwardness of the kind= specifier: its value has kind as well as type of the argument.
this is indeed "by design", one could define the constant of interest first and then use it in the initialization. For example:
COMPLEX*16, DIMENSION(4,1) :: INSTATE_BASISSTATES
REAL*8, PARAMETER :: my_const = 1D0 / SQRT(2D0)
INSTATE_BASISSTATES(:,:) = (0.0D0,0.0D0)
INSTATE_BASISSTATES(1,1) = (my_const,0.0D0)
INSTATE_BASISSTATES(3,1) = (my_const,0.0D0)
However, the statement REAL*8, PARAMETER :: my_const = 1D0 / SQRT(2D0) seems to require at least Fortran2003 standard, otherwise, following error is produced Elemental function as initialization expression with non-integer/non-character arguments. One can specify the standard with gfortran with -std=f2003 although it should be probably active by default.
#J123 still hasn't answered the pressing question. Are you writing your code in fixed-form format with .f extension or free-form .f90? Also, what version of gfortran are you using? I've posted a complete fix below. You can foliate your arrays by direct assignment using named parameter constants, or as the return value of the intrinsic transformational function cmplx. Please note the liberal use of the kind parameter wp to control the floating-point precision.
program main
use iso_fortran_env, only: &
wp => REAL64, & ! Or REAL128 if your architecture supports it
compiler_version, &
compiler_options
! Explicit typing only
implicit none
! Variable declarations
complex(wp) :: instate_basisstates(4,1)
real (wp), parameter :: ZERO = 0 ! Assigning integers is safe
real (wp), parameter :: SQRT2 = sqrt(2.0_wp)
real (wp), parameter :: ONE_OVER_SQRT2 = 1.0_wp/SQRT2
! Executable statements
instate_basisstates(:,:) = ZERO
instate_basisstates(1,1) = (ONE_OVER_SQRT2, ZERO)
instate_basisstates(3,1) = cmplx(1.0_wp/sqrt(2.0_wp), 0.0_wp, kind=wp)
print '(/4a/)', &
'This file was compiled using ', compiler_version(), &
' using the options ', compiler_options()
end program main
This yields:
This file was compiled using GCC version 6.1.1 20160802 using the options -mtune=generic -march=x86-64 -O3 -Wall -std=f2008ts

How does automatic typecasting (type conversion) work in Fortran?

I am using gfortran compiler. Also tell me if gfortran uses something other than the Fortran standard while performing automatic typecasting (type conversion).
Assignment is defined by Fortran 2008 Section 7.2. Of note is Cl. 7.2.1.3 paragraph 8:
For an intrinsic assignment statement where the variable is of numeric type, the expr may have a different numeric
type or kind type parameter, in which case the value of expr is converted to the type and kind type parameter
of the variable according to the rules of Table 7.9.
Table 7.9: Numeric conversion and the assignment statement
Type of variable Value Assigned
integer INT(expr , KIND = KIND (variable))
real REAL(expr , KIND = KIND (variable))
complex CMPLX(expr , KIND = KIND (variable))
This means that any expression (expr) will be implicitly converted to the type and kind of the variable it is being assigned to. For character types, derived types and anything else, please see the standard.
Also note that Fortran only performs conversions like this during assignment and initialization but not contexts like procedure calls. For example, consider this procedure:
subroutine sub1(a)
implicit none
integer :: a
print *, a
end subroutine
This procedure has a dummy argument of type integer. You cannot, for example, do this:
call sub1(1.d0)
because this results in a mismatch of type between the actual and dummy arguments.
You can, however, do this:
integer :: a
a = 1.d0 !implicitly interpreted as: a = INT(1.d0, kind=kind(a))
call sub1(a)
because the implicit conversion is defined for the assignment.
The only documented extension to the standard for implicit type conversion in gfortran (5.1.0) is between logical and integer types during assignment.
See: https://gcc.gnu.org/onlinedocs/gcc-5.1.0/gfortran/Implicitly-convert-LOGICAL-and-INTEGER-values.html#Implicitly-convert-LOGICAL-and-INTEGER-values
Logical .true. is converted to integer 1
Logical .false. is converted to integer 0
Integer 0 is converted to .false.
Any other integer is converted to .true.
Do note that if you can do without legacy extensions then don't use them. Using them means your program is not standard Fortran and thus any compiler is free to reject it for being incorrect. This extensions is meant to allow legacy code to compile with modern compilers and not for use in new code.

Convert logical type to double in Fortran

I'm looking for a bulletproof way of converting logical type variables to real type that will work in both ifort and gfortran. The following works in ifort, but not in gfortran:
logical :: a
real :: b
a = .true.
b = dble(a)
The error thrown in gfortran is
b = dble(a)
1
Error: 'a' argument of 'dble' intrinsic at (1) must be a numeric type
Obviously, .true. should map to 1.d0, and .false. to 0.d0. What's the best way of doing this?
In addition to writing a function to handle this, you could also directly use the intrinsic merge function: b = merge(1.d0, 0.d0, a). Or you could write a defined assignment subroutine that does this, so that you can just type b = a.
I am not sure if there is an intrinsic tool that does this. I do not know why ifort accepts this, and my guess would be that it is a compiler specific functionality.
Edit: As pointed out in https://stackoverflow.com/a/15057846/1624033 below, there is the intrinsic merge function which is exactly what is needed here.
an option to to this, specifically since you want this to be bullet proof, is to create your own function.
I have not tested this, but the following might work:
elemental pure double precision function logic2dbl(a)
logical, intent(in) :: a
if (a) then
logic2dbl = 1.d0
else
logic2dbl = 0.d0
end if
end function logic2dbl
Edit: I added elemental to the function declaration based on advice below. I also added pure to this function as it adds the extra ability to use this in parallel situations and it is good documentation. This however is just my opinion and it is not necessary.
In gfortran, I am using the TRANSFER intrinsic for that type of job.
Assuming a integer variable my_int then:
my_int = transfer(.false.,my_int)
the result of my_int is 0 as expected.
Just a note, TRANSFER(.true.,1) correctly returns the value of 1 with Gfortran and (incorrectly?) values of -1 with the current versions of Intel and Portland compilers. Interestingly, TRANSFER(-1,logical) returns TRUE with the latter two compilers while throws a syntax error with Gfortran.