2

I am converting a part of FORTRAN 77 code to C++

DIMENSION ARRAY(513),JRRAY(2,513)
EQUIVALENCE (ARRAY(1),JRRAY(1,1))

This is implicit code where every variable name starting with I,J,K,L,M,N,O,P are implicitly taken as integer type. Thus, here we have an double precision array named ARRAY and a integer array named JRRAY.

The equivalence statements points the start of both arrays to the same memory location. Somehow, however, the bytes are interpreted differently as double when ARRAY(I) is called or integers when JRRAY(I,J) is called (at least that is what I think what happens).

Is there a similar way in C++ where the same memory location can be interpreted as a different type?

Or something that does the same as EQUIVALENCE in FORTRAN, but then in C++.

  • 1
    Recall that in fortran arrays are stored in column major order so `JRRAY(1,X)` and `JRRAY(2,X)` point to the consecutive parts of `ARRAY(X)`. – Dima Chubarov May 23 '17 at 20:21
  • Do you mean actual reinterpretation of the representation, or automatic conversion? I.e. If I were to store pi in `ARRAY` and read it out of `JRRAY`, would I get `3` or some number corresponding to the bit pattern of the `double`? – Quentin May 23 '17 at 20:30
  • @Quentin FORTRAN simply reinterprets the representation, so that's presumably what he wants to do in C++. – Barmar May 23 '17 at 20:31
  • @Quentin, indeed, I wanted to reinterpret the representation. – user3615884 May 23 '17 at 20:48

2 Answers2

5

The analogous feature is a union:

union {
    double array[513];
    int jrray[513][2];
} equiv;

You can then access equiv.array[i] or equiv.jrray[i][j].

Note, however, that accessing a different member of the union than the one you last wrote to results in undefined behavior in C++. See Unions and type-punning. If you want to reinterpret data as a different data type, you should use reinterpret_cast<>, not type punning.

Barmar
  • 741,623
  • 53
  • 500
  • 612
  • Recall FORTRAN is column-major, so you have to switch the dimensions in jrray – Dima Chubarov May 23 '17 at 20:20
  • This is UB, so you should check that your implementation actually supports it. – Quentin May 23 '17 at 20:22
  • 2
    @Quentin It's only UB if you access a different member than you last wrote to. Unions by themselves are supported. I've added a link to another question that explains the issues with type punning in C and C++. – Barmar May 23 '17 at 20:24
  • @Barmar using both representations at the same time does sound like what OP is after, though. – Quentin May 23 '17 at 20:28
  • worked for me only comment I make is that double and int declarations should be switched. {double array[513]}, {int jrray[513][2]} – user3615884 May 23 '17 at 20:50
  • @user3615884 Oops, sorry about that. It makes more sense this way. – Barmar May 23 '17 at 20:52
  • It should be noted that this depends on `double` being 64 bit and `int` being 32 bit – Cole Tobin May 23 '17 at 22:44
  • A similar restriction applies in the Fortran original: defining an element of `array` causes the corresponding associated elements of `jrray` to become undefined (or single element if there's no rule making `array` `double precision`). – francescalus May 23 '17 at 23:01
  • @ColeJohnson Of course. A major reason why most languages do not define the results of type punning is because of all these implementation-dependent aspects. – Barmar May 24 '17 at 15:31
1

The C union is often used for this purpose as in the Barmar's answer. Yet you could use type casting to reference a floating point array as an integer array.

Consider the following declaration of array and definition of jrray:

double array[513];
int (*jrray)[2] = reinterpret_cast<int (*)[2]>(array);

We can check that this declaration works as expected for instance by looking at the exponent. We will have the exponent of array[k] in the bits 20-30 of jrray[k][1].

For example, check that if we now initialize the elements of array as

array[0] = 1.23*2; // exponent is 1
array[1] = 1.23*4; // exponent is 2
array[2] = 1.23*8; // exponent is 3

we will have

((jrray[0][1] >> 20) & 0x7FF) - 1023 == 1
((jrray[1][1] >> 20) & 0x7FF) - 1023 == 2
((jrray[2][1] >> 20) & 0x7FF) - 1023 == 3

Either way this violates C++ strict aliasing rule and could cause undefined behavior.

Dima Chubarov
  • 16,199
  • 6
  • 40
  • 76
  • 2
    Caution: this is just as undefined as the union trick above, but unlike that one, it is **not** typically defined by the implementation and will break at the slightest disturbance. See [here](https://godbolt.org/g/2EUDU7) for an example. – Quentin May 23 '17 at 21:49
  • @Quentin since the OP is porting FORTRAN code where function arguments may not be aliased he is probably safe. – Dima Chubarov May 23 '17 at 22:30
  • This is just an example. The compiler will happily assume that these are two distinct arrays if it allows it to make more optimizations (as it does here when inlining the function). You just cannot rely on it working. – Quentin May 23 '17 at 22:33