While np and nd are all integer 4 type, np*nd can be big enough can become integer 8 type. So if I need a variable jjj to represent np*nd, probably I need to define jjj as integer 8,
I mean if I have integer 4 and integer 8 types, I need to be careful enough and make sure when the product of integer 4 becomes big enough and requires integer 8. It is OK to use minimal data type in this way, but occasionally it may not be the most convenient perhaps.
So I wonder, how do you guys deal with such situation?
Do you just use integer 8 everywhere or something?
I found using integer 8 everywhere is perhaps not the most convenient either, and may very slightly slow down the code. So I am not exactly sure what is the best approach.
Most of the time, I just use the default kind, which I know will be at least 4 bytes on PC:
integer :: i
If I know or suspect that the value may become big (>2^31), I use integer(int64) (defined in iso_fortran_env). And int8 (8 bits) or int16 may be useful if memory matters (big integer arrays).
Considering speed, I am not sure int64 will be slower on nowadays 64 bits CPU.
In the original version of OP there was an array AAA(np,nd). I guess that internally, the processor should deal properly even with AAA(100000,100000) although the indices would overflow when simply multiplied.
In explicit calculations, however, one has to make sure there is no overflow and use proper kind(s). Note also, that with your declarations:
jjj will not be properly assigned if np*nd overflows in i4 kind. Consider sample program:
program main
use iso_fortran_env, only: i4=>int32, i8=>int64
integer(i4) :: i=100000, j=100000
integer(i8) :: bi,bj
bi = i*j ! overflows, assigns bad value
bj = int(i,kind=i8)*j ! converted to i8 before multiplication
print *, bi, bj
end program main
! outputs: 1410065408 10000000000
On WSL2 compiling the code above with gfortran -g -ftracer -ftrapv int_overflow.f90 and running I get
(base) /mnt/c/fortran/test$ ./a.out
Program received signal SIGABRT: Process abort signal.
Backtrace for this error:
#0 0x7f99f6416700 in ???
#1 0x7f99f64158a5 in ???
#2 0x7f99f622e20f in ???
#3 0x7f99f622e18b in ???
#4 0x7f99f620d858 in ???
#5 0x7f99f63dea41 in ???
#6 0x55db6d14a1b8 in MAIN__
at /mnt/c/fortran/test/int_overflow.f90:5
#7 0x55db6d14a297 in main
at /mnt/c/fortran/test/int_overflow.f90:8
Aborted
I use plain integer in my codes but should probably compile with -ftrapv to ensure that I am not missing integer overflow.
Sure if you add -ftrapv, signed overflow will cause exception. But w/o it, gfortran (ifort as well) produces code that silently ignores overflow giving bogus results. For ifort there seems to be no equivalent of -ftrapv. For other compilers I do not know.
I do not think any significant slowdown can be possible on 64-bit machines, 64-bit code. Unless your huge arrays (such that np*nd>2G) are integer also. Then you’ll get memory requirements really bigger.
Thank you @msz59 , your bi = i*j illustration is indeed great! Thank @Beliavsky too!
I did not realize that will cause an overflow, since bi on the left-hand-side is integer 8, I assume i*j should automatically be integer 8 and is calculated correctly.
But it seems it is not that intelligent and I need to be careful. Probably I may just use integer 8 instead.
A general rule of Fortran is that the RHS of an assignment is computed independently of what is on the LHS and then converted to that type. The product of two default integers is always a default integer.
I am too lazy to write unnecessarily more than integer :: and the code looks nice and simpler that way…
I am too old to waste 4 bytes without reason. I learned BASIC in the early 80’s on pocket computers with 1024 bytes RAM… Moreover, in the 90’s 32 bits integers were faster than 64 bits…
If anything, they have changed to make 32 bit integers more attractive. Data movement is what you pay for in time and energy and heat. If 32 bits are enough, it would be daft not to use them.