I have this code in a Fortran project:
ITEGER IV, IY
DIMENSION IV(NTAB,IDEPTH)
DIMENSION IY(IDEPTH)
DATA IV,IY /(IDEPTH*NTAB)*0,IDEPTH*0)/
Attempting to compile the project generates this error:
DATA IV,IY /(IDEPTH*NTAB)*0,IDEPTH*0)/
1
Syntax error in DATA statement at (1).
This worked under f77/g77 (gcc 4.1), but a recent upgrade has moved us to gcc 4.4 and gfortran. Now this code is causing errors but I just can't see the problem.
My guess is that this was an extension to the Standard, which is not supported any more. The FORTRAN 77 Standard, ch. 9.1 states that the repeat value shall be a
nonzero, unsigned, integer constant or the symbolic name of such a
constant.
As such, the IDEPTH*NTAB is not allowed as repeat value.
You can circumvent this by using another constant that constitutes the product:
PROGRAM test
INTEGER IV, IY
INTEGER,PARAMETER :: NTAB=1,IDEPTH=1
INTEGER,PARAMETER :: PROD=NTAB*IDEPTH
DIMENSION IV(NTAB,IDEPTH)
DIMENSION IY(IDEPTH)
DATA IV,IY /PROD*0,IDEPTH*0/
END
Or, to make it strictly FORTRAN 77 compliant:
PROGRAM test
INTEGER IV, IY
INTEGER NTAB,IDEPTH
INTEGER PROD
PARAMETER (NTAB=1,IDEPTH=1)
PARAMETER (PROD=NTAB*IDEPTH)
DIMENSION IV(NTAB,IDEPTH)
DIMENSION IY(IDEPTH)
DATA IV,IY /PROD*0,IDEPTH*0/
END
Related
How can I assign the boz-literal-constant Z'FEDCBA09' or any other bit-pattern with the most-significant bit equal to 1 to an integer?
The standard states:
INT(A[,KIND]): If A is a boz-literal-constant, the value of the result is the value whose bit sequence according to the model in 16.3 is the same as that of A as modified by padding or truncation according to 16.3.3. The interpretation of a bit sequence whose most significant bit is 1 is processor dependent.
source: Fortran 2018 Standard
So the following assignments might fail (assume integer is default 32 bit):
program boz
implicit none
integer :: x1 = int(Z'FEDCBA09')
integer :: x2 = int(Z'FFFFFFFF')
integer :: x3
data x3/Z'FFFFFFFF'/
end program
Using gfortran, this will only work when adding -fno-range-check but this introduces extra unwanted effects:
-fno-range-check: Disable range checking on results of simplification of constant expressions during compilation. For example, GNU Fortran will give an error at compile time when simplifying a = 1. / 0. With this option, no error will be given and a will be assigned the value +Infinity. If an expression evaluates to a value outside of the relevant range of [-HUGE():HUGE()], then the expression will be replaced by -Inf or +Inf as appropriate. Similarly, DATA i/Z'FFFFFFFF'/ will result in an integer overflow on most systems, but with -fno-range-check the value will "wrap around" and i will be initialized to -1 instead.
source: GNU Compiler Collection, gfortran manual
I attempted the following, which works fine but still not 100%
integer(kind=INT32) :: x1 = transfer(real(Z'FEDCBA09',kind=REAL32),1_INT32)
integer(kind=INT32) :: x1 = transfer(real(Z'FFFFFFFF',kind=REAL32),1_INT32)
The latter case fails with gfortran as it complains that Z'FFFFFFFF' represents NaN.
Using IOR(0,Z'FEDCBA09') also fails as it converts the boz-literal using INT
Question: How can you robustly assign a bit pattern using a boz-literal-constant? That is to say, independent of the used compiler (GNU, SUN, PGI, NAG, ...).
Answer: The most robust answer is currently given by Jim Rodes in this comment:
x = ior(ishft(int(Z'FEDC'),bit_size(x)/2),int(Z'BA09'))
This will work on any compiler and does not require any other data-type to be successful.
The need for -fno-range-check has been removed in what will be gfortran 10.1 when it is released. In 10.1, the bit patterns you have specified will be treated as if they are 32-bit unsigned integers and twos-complement wrap-around semantics are enforced.
Your first code snippet with a print statement added
program boz
implicit none
integer :: x1 = int(Z'FEDCBA09')
integer :: x2 = int(Z'FFFFFFFF')
integer :: x3
data x3/Z'FFFFFFFF'/
print *, x1, x2, x3
end program
yields
$ gfortran -o z file.f90
$ ./z
-19088887 -1 -1
and does not require the -fno-range-check option. The same goes for the proposed transfer method:
program boz
use iso_fortran_env
implicit none
integer(kind=INT32) :: x1 = &
& transfer(real(Z'FEDCBA09',kind=REAL32),1_INT32)
integer(kind=INT32) :: x2 = &
& transfer(real(Z'FFFFFFFF',kind=REAL32),1_INT32)
print '(I0,1X,Z8.8)', x1, x1
print '(I0,1X,Z8.8)', x2, x2
end program
returning:
$ gfortran -o z file.f90
$ ./z
-19088887 FEDCBA09
2143289344 7FC00000
Note: gfortran converts sNaN into qNan, which is a bug but no one cares.
If you are stuck with an older version of gfortran, then with the
integer case you need to use an intermediate conversion
program boz
use iso_fortran_env
implicit none
integer(kind=INT32) :: x1 = &
& transfer(int(Z'FEDCBA09',kind=INT64),1_INT32)
print '(I0,1X,Z8.8)', x1, x1
end program
gfortran will constant fold the statement with transfer. You can verify this by looking at the file created with the -fdump-tree-original option. For both this answer and the previous one, the command line is simple gfortran -o z file.f90.
When dealing with languages that do not support unsigned integers and you need to be able to test and/or set the high bit of the largest available integer, you can split the value into 2 variables and deal with the high order and low order bits separately.
One method would be to put the upper half into one variable and the lower half into another so that:
integer :: x1 = int(Z'FEDCBA09')
becomes:
integer :: x1Hi = int(Z'FEDC')
integer :: x1Lo = int(Z'BA09')
As the OP pointed out in an edit, a shift operation could then be used to assign the full value to a single variable like this. I changed it slightly so that it would work in case x is more than 32 bits.
x = ior(ishft(int(Z'FEDC'), 16), int(Z'BA09'))
Another possible method would be to have a separate variable for just the high bit.
I have asked a similar question before at comp.lang.fortran: https://in.memory.of.e.tern.al/comp.lang.fortran/thread/3878931
A practically usable, even though still the 100% probability was still questioned by some (see there) was just to use the reverse BOZ constant/string and NOT() it.
Instead of
integer, parameter :: i = Z'A0000000'
use
integer, parameter :: i = NOT(int(Z'5FFFFFFF'))
The analysis in the link goes to a large detail and to fine points of the standard and the numeric model interpretation.
Since then I use this in my production code: https://bitbucket.org/LadaF/elmm/src/master/src/rng_par_zig.f90 line 285 which is a translation of http://vigna.di.unimi.it/xorshift/xorshift128plus.c
I need to convert a parameter from integer(kind=8) to integer(kind=4) in Fortran, is there any (simple) way of doing this?
This parameter is an input number, and if this number is greater than 2^31-1 (the limit of a 4-byte integer), the program will always ask for a smaller number (so it can "fit" inside those 4 bytes), so I think that this shouldn't be a problem.
To create integer of any kind use
result = int(source, kind=result_kind)
so you can do
result = int(source, 4)
source can be any number, including an integer of any kind.
Note that kind=8 does not mean 8 bytes and kind=4 does not mean 4 bytes. There are compilers which do not have kinds 4 and 8 at all. These numbers are not portable. Do not use them. See Fortran: integer*4 vs integer(4) vs integer(kind=4) for more details.
As Vladimir F's answer notes, the intrinsic function int returns an integer value of desired kind int(i,kind=kind).
When an expression of a certain kind is required (such as in a procedure argument list) this is quite useful:
call sub_with_int_i1_arg(INT(int_i2, kind=i1))
However, intrinsic assignment to an integer already provides conversion if required:
integer, parameter :: kind1=7, kind2=23
integer(kind1) :: i
integer(kind2) :: j = 85
! The intrinsic assignment
i = j
! is equivalent to
i = INT(j,KIND(i))
! which here is also
i = INT(j,kind1)
end
The intrinsic huge may be useful in determining whether the range of i is large enough:
if (ABS(j).le.HUGE(i)) then
i = j
else
error stop "Oh noes :("
end if
As Steve Lionel commented about the draft, Fortran 2018 introduced the intrinsic function out_of_range which also tests such cases:
if (.not.OUT_OF_RANGE(j,i)) then
i = j
else
error stop "Oh noes :("
end if
However, even in early 2022 it's not wise to rely on implementation of this function.
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..
in C++ this is accepted:
uint64_t mask = 0x7FC0000FF80001FFLL;
but in fortran
integer(kind=8), parameter :: mask = Z'7FC0000FF80001FF'
does not work with gfortan.
I think both of them are 64bit values? or not?
gfortran complains:
arithmetic overflow from converting INTEGER(16) to INTEGER(8)
EDIT:
So, sorry for the confusion, here is some more extended problem description.
I will do some bit shifting in Fortran and have some sample code in c++.
There in the sample c++ code the masks are defines like:
typedef uint64_t mask;
static const mask dilate_2 = (mask)0x7FC0000FF80001FFLL ;
static const mask dilate_1 = (mask)0x01C0E070381C0E07LL ;
static const mask dilate_0 = (mask)0x9249249249249249LL ;
From my poor c++ understanding, I think that the hex values are 64bit
integer values (they have LL in the ending).
Now in Fortran my problem first was, that the definition with
integer(kind=8), parameter ...
did not work, as Vladimir said, because
integer(kind=8), ...
might be no 64bit integer.
Than I tested Alexanders solution, which works for the first and the
second (dilate_2, dilate_1) constant.
Also Vladimirs solution works for these two.
Now for dilate_0 none of these solutions work. I would suppose that Vladimirs solution will cast 0x9249249249249249LL (what is actually
a greater integer than allowed in INT64) into a INT64
if I do:
integer(INT64), parameter :: dilate_0 = int(Z'9249249249249249', &
kind=kind(dilate_0)
But this also don't work and gfortran give me an error:
Error: Arithmetic overflow converting INTEGER(16) to INTEGER(8) at (1).
So my actual question is how to implement this constant in Fortran?
As Vladimir posted in his comment integer(kind=8) is not portable (and not of kind 16 as the compiler complains).
As a remedy I suggest to use the intrinsic module ISO_Fortran_env (Fortran 2003) which has many predefined constants for the compiler used. Using INT64 from this module solves your problem and results in portable code:
program test
use,intrinsic :: ISO_Fortran_env, only: INT64
integer(INT64), parameter :: mask = Z'7FC0000FF80001FF'
print *,mask
end program
Z'9249249249249249' is not representable as a an INT64 (which is equivalent to an INTEGER(kind=8) in gfortran) because
BOZ constants are signed numbers (the same as every other integer constant in Fortran)
This number is larger than 2**63-1, the largest representable number for an INT64
Gfortran therefore selects the smallest integer type which fits, which is INTEGER(KIND=16).
We then have parameter staement where an INTEGER(KIND=8) parameter should be assigned a value outside its range. This is what the compiler complains about. It would complain the same way about
INTEGER(KIND=4), PARAMETER :: N = 37094947285
If you want to get around this, you can use the -fno-range-check option to gfortran. Information about -fno-range-check is already included in the gfortran error message (the part you didn't show).
I would do this to stay standard conforming
integer(whatever), parameter :: mask = int(Z'7FC0000FF80001FF', &
kind=kind(mask))
where whatever is some kind constant of the required value. It could be int64.
The above will not work if the constant corresponds to a negative number. One then has to make a trick like:
integer(int32), parameter :: mask = transfer(int(Z'A0000000',int64),1_int32)
or
integer(int32), parameter :: mask = transfer(Z'A0000000',1_int32)
but I am not sure whether the last one is strictly standard conforming.
When compiled with either GNU Fortran (v4.4.3) or Sun Studio F95 (v8.3) and no array bounds checking the following program runs without error. However, when array bounds checking is switched on (gfortran -fbounds-check and f95 -C, respectively) the GNU compiled executable runs again without error, whereas the Sun Studio compiled executable gives the run-time error,
****** FORTRAN RUN-TIME SYSTEM ******
Subscript out of range. Location: line 44 column 20 of 'nosize.f90'
Subscript number 2 has value 1 in array 't$27'
That's an error in the call to sub2(), which uses an automatic array dummy argument for x. The sub1() calls run fine with either compiler and any flags.
To my knowledge this program is "legal", in that a zero sized array may be referenced like a non-zero sized array, and there is no explicit indexing of the zero length dimension of x. But is there some zero sized array slicing or automatic array subtlety that I'm missing here? And should I expect array bounds checking to behave the same across different compilers, or should I consider it a vendor-specific extension?
MODULE subs
IMPLICIT NONE
CONTAINS
SUBROUTINE sub1(x)
IMPLICIT NONE
REAL :: x(:,:)
PRINT*,'------------------------------------'
PRINT*,SHAPE(x)
PRINT*,SIZE(x)
END SUBROUTINE sub1
SUBROUTINE sub2(n1,n3,x)
IMPLICIT NONE
INTEGER,INTENT(in) :: n1, n3
REAL :: x(n1,n3)
PRINT*,'------------------------------------'
PRINT*,SHAPE(x)
PRINT*,SIZE(x)
END SUBROUTINE sub2
END MODULE subs
PROGRAM nosize
USE subs
IMPLICIT NONE
INTEGER :: n1 = 2, n2 = 2, n3 = 0
REAL,ALLOCATABLE :: x(:,:,:)
ALLOCATE(x(n1,n2,n3))
x(:,:,:) = -99.9
PRINT*,'ALLOCATED? ',ALLOCATED(x)
PRINT*,'SHAPE =',SHAPE(x)
PRINT*,'SIZE =',SIZE(x)
PRINT*,'X =',x
CALL sub1(x(:,1,:))
CALL sub2(n1,n3,x(:,1,:))
END PROGRAM nosize
It doesn't give any problems with intel's fortran compiler with -check bounds; and IBM's xlf, which in my experience is extremely strict, also didn't complain with -qcheck.
But more broadly, yes, there's no standard about what bounds checking should or shouldn't do. I can certainly see why some compilers would flag an assignment to a zero-length array as being bad/wrong/weird; it is a strange corner-case.