Complex constants and variables

Inspired by the redundant parentheses thread, I looked up the rules for complex literals.

If both parts of the literal constant are of type real, the kind type parameter of the literal constant is the kind parameter of the part with the greater precision, and the kind type parameter of the part with lower precision is converted to that of the other part.

If both parts are of type integer, they are each converted to type default real. If one part is of type integer and the other is of type real, the integer is converted to type real with the precision of the real part.

They surprised me. Good tweet material :slight_smile: . Here is an illustrative program.

program complex_numbers
implicit none
integer, parameter :: dp = kind(1.0d0)
real :: r1=2.0, r2 = 3.0
real, parameter :: c1 = 2.0, c2 = 3.0
real(kind=dp), parameter :: d1 = 2.0_dp, d2 = 3.0_dp
! line below invalid since r1 and r2 are not constants
! print*,(r1,r2) 
print*,"r1,r2",r1,r2
print*,"c1,c2",c1,c2
print*,"d1,d2",d1,d2
print*,"(c1,c2)",(c1,c2)
print*,"(2.0,3.0)",(2.0,3.0)
print*,"(2,3)",(2,3),"integers converted to default real in complex constructor"
print*,"(2,3.0)",(2,3.0),"both parts of complex number are default real"
print*,"(2.0d0,3.0)",(2.0d0,3.0),"both parts have the higher precision"
print*,"cmplx(r1,r2)",cmplx(r1,r2),"complex number where the real and imaginary parts are variables"
print*,"cmplx(d1,d2)",cmplx(d1,d2),"single precision complex number, because no kind specified!"
print*,"cmplx(d1,d2,kind=dp)",cmplx(d1,d2,kind=dp),"double precision complex number"
end program complex_numbers

output:

 r1,r2 2.00000000  3.00000000 
 c1,c2 2.00000000  3.00000000 
 d1,d2 2.0000000000000000  3.0000000000000000  
 (c1,c2)  (2.00000000,3.00000000)
 (2.0,3.0)  (2.00000000,3.00000000)
 (2,3)  (2.00000000,3.00000000) integers converted to default real in complex constructor
 (2,3.0)  (2.00000000,3.00000000) both parts of complex number are default real
 (2.0d0,3.0)  (2.0000000000000000,3.0000000000000000) both parts have the higher precision
 cmplx(r1,r2)  (2.00000000,3.00000000) complex number where the real and imaginary parts are variables
 cmplx(d1,d2)  (2.00000000,3.00000000) single precision complex number, because no kind specified!
 cmplx(d1,d2,kind=dp)  (2.0000000000000000,3.0000000000000000) double precision complex number
1 Like

which leads to this interesting result. you can construct con3 from con1 and con2
and use it to define a complex constant, but if you put the expression used to create
con3 into the definition of a complex constant (cx2) it is non-standard; which is a little
non-intuitive but correct; even though some compilers allow it, so if you want to use an expression to create the constant, you have to define it as a literal constant first of say, type real; where you can use an expression (as in the definition of con3).

program testit
real,parameter :: con1=10.0, con2=20.0
real,parameter :: con3=con1*con2
complex :: cx1=(con3,30.0)
!!complex :: cx2=(con1*con2,30.0) ! bad: gfortran, ifort; good: nvfortran
write(*,*)cx1
!!write(*,*)cx2
end program testit

A better illustration perhaps is

NOT STANDARD

program testit
!! THIS IS NOT STANDARD
! bad: gfortran, ifort; good: nvfortran
complex :: cx2=(sin(40.0),cos(40.0)) 
write(*,*)cx2
end program testit

STANDARD

program testit
!! THIS IS
real,parameter :: r1=sin(40.0), r2=cos(40.0)
complex :: cx2=(r1,r2)
write(*,*)cx2
end program testit

QUIZ

So what will this do (hint: depends on which standard level)?

program testit
complex :: cx2=cmplx(sin(40.0),cos(40.0)) 
write(*,*)cx2
end program testit

It will be default complex value in F2003+. Before that, elemental intrinsic functions with non-integer/non-character arguments/results were forbidden in initializers.

Which one(s) did? The rules are pretty standard, conforming to what is being done when two arguments of different types/kinds meet in an arithmetic operation.

The only thing which might seem surprising is (in your snippet, not in the conversion rules) the result of
cmplx(d1,d2) being default complex. But cmplx is a conversion function, analogous to real, coming from times of F77 Standard in which there was no kind or double precision complex type. Accordingly, there was REAL()/DBLE() pair but only CMPLX().

1 Like

Nice summary from my view. I always have to double-check cmplx() calls have a kind parameter if not using default real; and until I found someone else using it it had not occurred to me cmplx could take just one value. So I have been surprised by

program testit
write(*,*)cmplx(10)  ! did not know a single value was legal for a long time
write(*,*)cmplx(20.0d0,20.0d0) ! had to learn this returns default real kind
write(*,*)(20.0d0,20.0d0)  ! and so was not sure this would return double
end program testit

and I think %im and %re were around for a long time before I knew it, and even longer before I knew they would be used on the LHS; although I am not sure that is a common miss.

x%im=10.0d0
x%re=30.0d0

those were my personal surprises. I hate cmplx() defaulting to default real enough I was using my own generic function just called COMPLEX for a while; but it ends up some compilers have a procedure of the same name, and so it was confusing.

It was so in F77 (or, maybe, earlier). As it is a type conversion function, it could always take any arithmetic type argument. One (then the imaginary part was set to 0.0) or two, with the obvious exception of only single complex argument allowed.

I always specify the second value, perhaps a quirk but I prefer to. I (think) I know the rules now, but I do remember some struggles with complex values. Hard to remember them all now but for a tweet there might be some fodder here:

program testit
integer,parameter :: dp=kind(0.0d0)
complex :: c1
real(kind=dp) :: d1=100.0d0, d2=200.0d0
complex(kind=dp) :: c2(3)

write(*,*)cmplx(10) ! one value is allowed

write(*,*)cmplx(20.0d0,20.0d0) ! this is default kind
write(*,*)cmplx(20.0d0,20.0d0,kind=dp) ! this gets doubleprecision
! this gets doubleprecision using %kind if compiler has this yet
write(*,*)cmplx(d1,d2,d1%kind) 

c1=(30,40)  ! allowed with constants
write(*,*)c1
!c2=(d1,d2) ! but cannot do this
c1=(20+3) ! but you can do this because it is an expression 
          ! and () is actually not defining a complex value. It is the same as "c1=20+3"
write(*,*)c1,(20+3)

c1%re=d1; c1%im=d2
write(*,'(g0,"+i",g0)')c1 ! you have to define two fields for complex
write(*,*) c1  ! note the parenthesis
write(*,'(g0)') ! g0 and * produce very different results with complex, unlike with most other values

end program testit

Your descriptions are great, by the way. WIsh some textbooks were as clear.

1 Like