# Complex type storage (again)

**URL:** https://fortran-lang.discourse.group/t/complex-type-storage-again/7020
**Category:** Help
**Created:** [December 20, 2023, 3:31pm UTC](https://fortran-lang.discourse.group/t/complex-type-storage-again/7020 "2023-12-20T15:31:18Z")
**Posts on this page:** 20
**Page:** 1

<div class="post-metadata">

### Author: ![PierU](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/pieru/32/1848_2.png) [@PierU](https://fortran-lang.discourse.group/u/PierU)
#### Post date: [December 20, 2023, 3:31pm UTC](https://fortran-lang.discourse.group/t/complex-type-storage-again/7020/1 "2023-12-20T15:31:18Z")

</div>

This is a recurrent two-part question:

1. does the standard require a complex variable to be stored as 2 adjacent real number of the same kind as the complex variable?
2. assuming 1., does the standard require these 2 stored real numbers to be exactly the real and imaginary components of the complex variable?
3. assuming 2., does the standard require the real part to be stored first?

I can find in the [F2018 standard](https://j3-fortran.org/doc/year/18/18-007r1.pdf):

- A: 7.1.3  
_The set of valid values for complex consists of the set of all the combinations of the values of the real and imaginary parts_
- B: 7.4.3.3 (1)  
_The complex type has values that approximate the mathematical complex numbers. The values of a complex type are ordered pairs of real values. The first real value is called the real part, and the second real value is called the imaginary part._
- C: 7.4.3.3 (2)  
_Each approximation method used to represent data entities of type real shall be available for both the real and imaginary parts of a data entity of type complex. The (default integer) kind type parameter KIND for a complex entity specifies for both parts the real approximation method characterized by this kind type parameter value._
- D: 19.5.3.2 (2) (2)  
_a nonpointer scalar object that is double precision real or default complex occupies two contiguous numeric storage units_
- E: 19.6.5 (13+14)  
_When a default complex entity becomes defined, all partially associated default real entities become defined. When both parts of a default complex entity become defined as a result of partially associated default real or default complex entities becoming defined, the default complex entity becomes defined_

Given all this, I can’t see how a compiler could implement a complex number other than by storing the the real and imaginary components with the same kind. In other words the answer is yes for the questions (1) and (2). This excludes different number of bits between the two components, or a storage of the modulus+angle, or whatever…

Regarding question (3) I’m not convinced. Citation (B) talks about an _ordered_ pair, the first value being the real component, but without any reference to the storage. i.e. nothing really says that `r(1)` and `c%re` share the same numeric storage unit:

```auto
real :: r(2)
complex :: c
equivalence (r,c)

```

What do you think?

---

<div class="post-metadata">

### Author: ![msz59](https://avatars.discourse-cdn.com/v4/letter/m/3d9bf3/32.png) [@msz59](https://fortran-lang.discourse.group/u/msz59)
#### Post date: [December 20, 2023, 3:53pm UTC](https://fortran-lang.discourse.group/t/complex-type-storage-again/7020/2 "2023-12-20T15:53:17Z")

</div>

IMHO

1. Yes (items B and D of your Standard quotes)
2. Yes (B (_real part_ and _imaginary part_, also A, as any other representation (say, polar) would likely break the A requirement)
3. Yes (B (_ordered_, _first … real_, _second … imaginary_); if it allowed the opposite sequence, the term _ordered_ would have no useful meaning)

> [@PierU](#):
>
> without any reference to the storage

How come? _default complex occupies two contiguous numeric storage units_ (actually you copy-pasted _16_ which is the row number

---

<div class="post-metadata">

### Author: ![PierU](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/pieru/32/1848_2.png) [@PierU](https://fortran-lang.discourse.group/u/PierU)
#### Post date: [December 20, 2023, 5:05pm UTC](https://fortran-lang.discourse.group/t/complex-type-storage-again/7020/3 "2023-12-20T17:05:45Z")

</div>

> [@msz59](#):
>
> Yes (B (_ordered_, _first … real_, _second … imaginary_); if it allowed the opposite sequence, the term _ordered_ would have no useful meaning)

I’m not sure… For instance, the integer model in the standard talks about ordered (indexed) bits, but without specifying how they actually stored. And as a matter of fact they are not stored in the same order on big-endian and on little-endian machines.

---

<div class="post-metadata">

### Author: ![msz59](https://avatars.discourse-cdn.com/v4/letter/m/3d9bf3/32.png) [@msz59](https://fortran-lang.discourse.group/u/msz59)
#### Post date: [December 20, 2023, 5:20pm UTC](https://fortran-lang.discourse.group/t/complex-type-storage-again/7020/4 "2023-12-20T17:20:15Z")

</div>

Well, so maybe that’s the reason for declaring `equivalence` obsolescent.  
On the other hand, implementing `complex` the _backwards way_ would probably break quite a few old codes.

---

<div class="post-metadata">

### Author: ![PierU](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/pieru/32/1848_2.png) [@PierU](https://fortran-lang.discourse.group/u/PierU)
#### Post date: [December 20, 2023, 5:30pm UTC](https://fortran-lang.discourse.group/t/complex-type-storage-again/7020/5 "2023-12-20T17:30:25Z")

</div>

> [@msz59](#):
>
> On the other hand, implementing `complex` the _backwards way_ would probably break quite a few old codes.

Sure, and no compiler vendor would do that now. All the more than there would be no specific advantage by storing the other way.

But at some point I think that this de-facto standard should be clearly integrated in the standard one way or another. And that (this is actually my point) a real pointer should be allowed to point a complex target, and vice-versa (and this requires a non ambiguous storage order).

---

<div class="post-metadata">

### Author: ![jeff.irwin](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/jeff.irwin/32/3673_2.png) [@jeff.irwin](https://fortran-lang.discourse.group/u/jeff.irwin)
#### Post date: [December 20, 2023, 6:10pm UTC](https://fortran-lang.discourse.group/t/complex-type-storage-again/7020/6 "2023-12-20T18:10:12Z")

</div>

Sorry for the tangent, but it just occurred to me that Fortran does not have a type for the [Gaussian integers](https://en.wikipedia.org/wiki/Gaussian_integer) (basically complex numbers but only with integer components)

I’m not sure how many applications Gaussian integers have, but it seems strange for a language to have a complex (float) type and not a complex integer type

---

<div class="post-metadata">

### Author: ![msz59](https://avatars.discourse-cdn.com/v4/letter/m/3d9bf3/32.png) [@msz59](https://fortran-lang.discourse.group/u/msz59)
#### Post date: [December 20, 2023, 6:48pm UTC](https://fortran-lang.discourse.group/t/complex-type-storage-again/7020/7 "2023-12-20T18:48:50Z")

</div>

> [@PierU](#):
>
> a real pointer should be allowed to point a complex target,

You can have some sort of that already now:

```fortran
program test
  implicit none
  complex, target :: c
  real, pointer :: rr, ri
  rr => c%re
  ri => c%im
  c = (1.0, 2.0)
  print *, rr ! ===> 1.00000000
end program test

```

---

<div class="post-metadata">

### Author: ![jkd2022](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/jkd2022/32/1949_2.png) [@jkd2022](https://fortran-lang.discourse.group/u/jkd2022)
#### Post date: [December 20, 2023, 7:32pm UTC](https://fortran-lang.discourse.group/t/complex-type-storage-again/7020/8 "2023-12-20T19:32:19Z")

</div>

> But at some point I think that this de-facto standard should be clearly integrated in the standard one way or another. And that (this is actually my point) a real pointer should be allowed to point a complex target, and vice-versa (and this requires a non ambiguous storage order).

Agreed. We use this in our code:

```auto
do j=1,n
  do i=1,j-1
    c(i,j)=c(i,j)+zdotc(l,a(1,i),1,b(1,j),1)
  end do
  c(j,j)=c(j,j)+ddot(2*l,a(1,j),1,b(1,j),1)
end do

```

to set up a Hermitian matrix. The diagonal elements are real so ddot can be used as a speed-up. However, it relies on consecutive storage of real and imaginary parts. Also, you have to ‘hide’ this from the compiler by avoiding an explicit interface or using ddot with real arrays in the same routine. Most unsatisfactory.

---

<div class="post-metadata">

### Author: ![RonShepard](https://avatars.discourse-cdn.com/v4/letter/r/a3d4f5/32.png) [@RonShepard](https://fortran-lang.discourse.group/u/RonShepard)
#### Post date: [December 20, 2023, 8:51pm UTC](https://fortran-lang.discourse.group/t/complex-type-storage-again/7020/9 "2023-12-20T20:51:17Z")

</div>

> [@PierU](#):
>
> When a default complex entity becomes defined, all partially associated default real entities become defined. When both parts of a default complex entity become defined as a result of partially associated default real or default complex entities becoming defined, the default complex entity becomes defined

One wonders why this is limited to a default complex entity. Shouldn’t it apply equally to a complex entity of any KIND?

In f77, all possible combinations of storage sequence association involving integer, real, logical, double precision, and complex were defined for scalars and arrays with equivalence and for common blocks. F77 avoided the storage sequence association of character and noncharacter entities. F90 and later introduced the KIND system for all of these intrinsic data types, but these later standards have tended to avoid the long discussions of storage sequence association, hence my question above about the apparently intentional use of “default”.

---

<div class="post-metadata">

### Author: ![PierU](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/pieru/32/1848_2.png) [@PierU](https://fortran-lang.discourse.group/u/PierU)
#### Post date: [December 21, 2023, 12:38pm UTC](https://fortran-lang.discourse.group/t/complex-type-storage-again/7020/10 "2023-12-21T12:38:32Z")

</div>

> [@msz59](#):
>
> You can have some sort of that already now:
> 
> ```auto
> rr => c%re
> ri => c%im
> 
> ```

Yes, but it does not cover the typical use case where one has a real array, on which a real-to-complex FFT is performed in place. Before FFT and after inverse FFT one wants to deal with real number, or with complex numbers in between. There’s a trick with `c2f_pointer()` but this is not really satisfactory (and non standard AFAIK):

```auto
program test
  implicit none
  complex, pointer :: c(:)
  real, allocatable, target :: r(:)
  !...
  call rfft(r)
  call c2f_pointer(c_loc(r),c,[size(r)/2])
  !...
end program test

```

---

<div class="post-metadata">

### Author: ![msz59](https://avatars.discourse-cdn.com/v4/letter/m/3d9bf3/32.png) [@msz59](https://fortran-lang.discourse.group/u/msz59)
#### Post date: [December 21, 2023, 1:09pm UTC](https://fortran-lang.discourse.group/t/complex-type-storage-again/7020/11 "2023-12-21T13:09:08Z")

</div>

why not use obsolescent, yet still valid, `equivalence`?

---

<div class="post-metadata">

### Author: ![PierU](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/pieru/32/1848_2.png) [@PierU](https://fortran-lang.discourse.group/u/PierU)
#### Post date: [December 21, 2023, 1:55pm UTC](https://fortran-lang.discourse.group/t/complex-type-storage-again/7020/12 "2023-12-21T13:55:21Z")

</div>

```auto
real, allocatable :: r(:)
complex, allocatable :: c(:)
equivalence (r,c)
end

```

```auto
    3 | equivalence (r,c)
      | 1
Error: EQUIVALENCE attribute conflicts with ALLOCATABLE attribute in 'r' at (1)

```

And anyway, using equivalence for this use case is also a trick that is not formally supported by the standard. IMO the standard should clearly state the storage association between REAL and COMPLEX, and provide specific intrinsics:

```auto
real, allocatable :: r(:)
complex, pointer :: c(:)
allocate( r(n) )
c => cmplx_pointer(r)
end

```

and the other way:

```auto
real, pointer :: r(:)
complex, allocatable :: c(:)
allocate( c(n) )
r => real_pointer(c)
end

```

---

<div class="post-metadata">

### Author: ![RonShepard](https://avatars.discourse-cdn.com/v4/letter/r/a3d4f5/32.png) [@RonShepard](https://fortran-lang.discourse.group/u/RonShepard)
#### Post date: [December 21, 2023, 5:33pm UTC](https://fortran-lang.discourse.group/t/complex-type-storage-again/7020/13 "2023-12-21T17:33:37Z")

</div>

> [@PierU](#):
>
> There’s a trick with `c2f_pointer()` but this is not really satisfactory (and non standard AFAIK):

Doesn’t this accomplish the same thing as your later suggestion:

```auto
c => cmplx_pointer(r)
...
r => real_pointer(c)

```

What exactly is nonstandard with `c2f_pointer()`? And even if it is nonstandard, what could possibly go wrong?

---

<div class="post-metadata">

### Author: ![PierU](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/pieru/32/1848_2.png) [@PierU](https://fortran-lang.discourse.group/u/PierU)
#### Post date: [December 21, 2023, 5:51pm UTC](https://fortran-lang.discourse.group/t/complex-type-storage-again/7020/14 "2023-12-21T17:51:16Z")

</div>

> [@RonShepard](#):
>
> What exactly is nonstandard with `c2f_pointer()`? And even if it is nonstandard, what could possibly go wrong?

The standard explictely says (18.2.3.3 of the F2018 standard) that in  
`call c_f_pointer(c_loc(r),c,[size(r)/2])`  
`r` and `c` shall have the same type, which is not the case here.

True, I can’t see either what could go wrong, but relying on a non-standard feature is not really satisfactory. And relying on `iso_c_binding` while no C code at all is involved is not really satisfactory either.

---

<div class="post-metadata">

### Author: ![ivanpribec](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/ivanpribec/32/3290_2.png) [@ivanpribec](https://fortran-lang.discourse.group/u/ivanpribec)
#### Post date: [December 21, 2023, 6:10pm UTC](https://fortran-lang.discourse.group/t/complex-type-storage-again/7020/15 "2023-12-21T18:10:24Z")

</div>

> [@PierU](#):
>
> `call c2f_pointer(c_loc(r),c,[size(r)/2])`

The function is called `c_f_pointer`, before anyone gets confused.

Otherwise, I fully agree with your points. This use case is common enough that it warrants some type of support from the standard, beyond using C binding.

It’s nice that the standard allows accessing the strided sub-array (or is this just for scalars?):

```fortran
  rr => c%re
  ri => c%im

```

Maybe the syntax could be:

```auto
c(1:size(r)/2) => complex :: r

```

What would happen in case `size(r)` is an **odd** number? Should the standard require that the size of `r` is even, if a complex pointer is to be associated with a real target? Would it be okay to leave one “dangling” element?

---

<div class="post-metadata">

### Author: ![RonShepard](https://avatars.discourse-cdn.com/v4/letter/r/a3d4f5/32.png) [@RonShepard](https://fortran-lang.discourse.group/u/RonShepard)
#### Post date: [December 21, 2023, 7:45pm UTC](https://fortran-lang.discourse.group/t/complex-type-storage-again/7020/16 "2023-12-21T19:45:17Z")

</div>

> [@PierU](#):
>
> The standard explictely says (18.2.3.3 of the F2018 standard) that in  
> `call c2f_pointer(c_loc(r),c,[size(r)/2])`  
> `r` and `c` shall have the same type, which is not the case here.

Ah, you are right. It seems like there should be exceptions for the cases where storage sequence association is already defined by the standard. In this case, the KIND values even match.

> [@ivanpribec](#):
>
> This use case is common enough that it warrants some type of support from the standard, beyond using C binding.

Using C binding features without any actual C code bothered me too 20 years ago (f2003). Now I don’t even think about it. I’m just glad it is there.

> [@](#):
>
> It’s nice that the standard allows accessing the strided sub-array (or is this just for scalars?):

It is allowed for arrays, however I remember a discussion from a couple of years ago where some compilers do copy-in/copy-out argument association instead of the array slice association, so there might be some performance/efficiency issues with using %re and %im for arrays.

---

<div class="post-metadata">

### Author: ![PierU](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/pieru/32/1848_2.png) [@PierU](https://fortran-lang.discourse.group/u/PierU)
#### Post date: [December 22, 2023, 8:43am UTC](https://fortran-lang.discourse.group/t/complex-type-storage-again/7020/17 "2023-12-22T08:43:37Z")

</div>

> [@RonShepard](#):
>
> Using C binding features without any actual C code bothered me too 20 years ago (f2003). Now I don’t even think about it. I’m just glad it is there.

Apart that it’s a kind of "thank god, we have the C stuff to get Fortran working 🙂 ", having a full Fortran support would be safer. `c_f_pointer` is convenient but you are on your own (no possibly check of the kinds, of the contiguity, etc…)

---

<div class="post-metadata">

### Author: ![themos](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/themos/32/521_2.png) [@themos](https://fortran-lang.discourse.group/u/themos)
#### Post date: [December 22, 2023, 11:53am UTC](https://fortran-lang.discourse.group/t/complex-type-storage-again/7020/18 "2023-12-22T11:53:01Z")

</div>

> [@PierU](#):
>
> does the standard require a complex variable to be stored as 2 adjacent real number of the same kind as the complex variable?

My answer is no. The memory system is abstracted away in Fortran. There is no requirement to map the concepts of Fortran to a linearly addressed store. We can run Fortran programs in our heads and I don’t think we have such a store. Having said that, the sanest way of implementing the rules of storage association is what we usually find in implementations, because the available hardware has a linearly addressed store.

Here’s a basic rule of storage association:

_When a scalar variable of intrinsic type becomess (sic) defined, all totally associated variables of different type become undefined._

Your “A: 7.1.3” and “B: 7.4.3.3 (1)” are about the set of values and the ordering mentioned applies to syntactic forms in the language, such as the literal **(1.0,2.0)**.

Your “C: 7.4.3.3 (2)” is about the relation of a REAL KIND to a COMPLEX KIND.

Your “D: 19.5.3.2 (2)” specifies the accounting of storage sequences (number and type) for COMPLEX. Nothing more.

Your “E: 19.6.5 (13+14)” specifies which real (or complex) entities become defined when associated complex (or real) entities become defined, but does **not** specify what value these entities acquire.

> nothing really says that `r(1)` and `c%re` share the same numeric storage unit:

Indeed, the statement

> EQUIVALENCE(r(1),c%re)

is not allowed (complex-part-designator is not one of the options listed for equivalence-object).

I am inclined to believe that storing complex values as 64 interleaved bits from an IEEE bin32 encoding modulus and a 32-bit integer encoding cos(θ) may be insane but doable.

---

<div class="post-metadata">

### Author: ![PierU](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/pieru/32/1848_2.png) [@PierU](https://fortran-lang.discourse.group/u/PierU)
#### Post date: [December 22, 2023, 12:43pm UTC](https://fortran-lang.discourse.group/t/complex-type-storage-again/7020/19 "2023-12-22T12:43:20Z")

</div>

> [@themos](#):
>
> I am inclined to believe that storing complex values as 64 interleaved bits from an IEEE bin32 encoding modulus and a 32-bit integer encoding cos(θ) may be insane but doable.

This is what is I was thinking too, until I realized in this thread the implications of

```auto
rr => c(:)%re
ri => c(:)%im

```

This seems to be valid Fortran, and I can’t see how it could work if a complex was not stored as non interleaved real and imaginary parts.

Besides, by storing the modulus + angle, I can’t see how (A), (B), and (C) could be honored. For instance, if both `cmplx(huge(0.0),0.0)` and `cmplx(0.0, huge(0.0))` are valid complex numbers, (A) says that `cmplx(huge(0.0),huge(0.0))` must be a valid complex number too. This is not possible with a modulus+angle storage.

---

<div class="post-metadata">

### Author: ![themos](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/themos/32/521_2.png) [@themos](https://fortran-lang.discourse.group/u/themos)
#### Post date: [December 22, 2023, 5:04pm UTC](https://fortran-lang.discourse.group/t/complex-type-storage-again/7020/20 "2023-12-22T17:04:47Z")

</div>

It looks like you can either have the TARGET attribute or be able to participate in EQUIVALENCE, but not both. I think you can follow the “insane” spec above and satisfy the requirements of the Standard, but you would have to have more room in the runtime baggage of POINTER variables to handle the case where you want to remember that you are supposed to be converting to Cartesian coordinates and take the real-coordinate when you have to supply the target. In principle there is no limit to the amount of semantic information you can carry along at runtime. Efficiency dictates where you stop.

[Next page](https://fortran-lang.discourse.group/t/complex-type-storage-again/7020.md?page=2)
