I have a set of code in which I declare the size of all my variables. However, I want to change them so some of the variables are allocatable.
This is (what I believe) to be the relevant code of the original.
parameter (m=10**5,nchar=2000,ncols=110)
character (LEN=*), PARAMETER :: FORM = "(A)"
character(LEN=nchar),DIMENSION(m) :: data
* determine the length of the data
nlines = 0
do i = 1,m
read(12,*,end=10)
nlines = nlines+1
end do
nlines = nlines+1
* read in the data
10 REWIND(unit = 12)
do i = 1,nlines
read(12,FORM) data(i)
end do
This is the form I would like.
parameter (m=10**5,nchar=2000,ncols=110)
character (LEN=*), PARAMETER :: FORM = "(A)"
character(LEN=:),DIMENSION(:),allocatable :: data
integer j
character(LEN=100000) :: temp
* determine the length of the data
nlines = 0
j = 0
do while (ios == 0)
read(12,FORM,end=10,iostat=ios) temp
if (LEN(TRIM(temp)) .gt. j) THEN
j = LEN(TRIM(temp))
end if
nlines = nlines+1
end do
nlines = nlines+1
10 REWIND(unit = 12)
ALLOCATE (character(LEN=j) :: data(nlines))
* read in the data
do i = 1,nlines
read(12,FORM) data(i)
end do
Problematically, this causes an integer overflow error. "Fortran runtime error: Integer overflow when calculating the amount of memory to allocate". I find this very perplexing as the 'nlines' and 'j' values I obtain from my test dataset are only 10 and 110 respectively.
I have found that if I don't allocate the character length (i.e. keep it a constant), this works.
I am using gfortran, version 4.8.0.
What is wrong with my code/method?
Related
I have the following code and I would like to save the value of idx to use afterwards.
program use_value_allocatable
implicit none
integer :: i, ii
integer, dimension(3) :: array_save
character(1), dimension(3) :: array_char_ref = (/'a','b','c'/), array_char_1 = (/'c','a','b'/)
integer, allocatable :: idx(:)
array_save = 0
do i = 1, 3
idx = pack([(ii,ii=1,3)], array_char_ref == array_char_1(i) )
print*, 'i=', i, ', idx=', idx, ', array_save(i) =', array_save(i)
!!$ array_save(i) = idx
deallocate(idx)
end do
end program use_value_allocatable
Having array_save(i) = idx in the code leads to an error as follows:
Error: Incompatible ranks 0 and 1 in assignment at (1)
So, I can conclude that I cannot use the value of an allocatable variable (here idx). How I can circumvent this problem?
P.S.: in this example I assume that idx will always be an integer of dimension 1
As you say, "idx will always be an integer of dimension 1", so you just need
array_save(i) = idx(1)
i.e. you need to store the first (and only) element of idx in array_save(i), rather than the whole array.
A side note: you do not need the line deallocate(idx). idx will be implicitly re-allocated by the line idx = ..., and will be automatically deallocated when it drops out of scope.
Im trying to print prime numbers till 10000. (display the first five for testing)
This is my program
program primes
implicit none
integer :: array(1229)
integer :: i, ind
logical :: is_prime
ind = 1
do i = 2, 10000, 1
if (is_prime(i) .eqv. .true.) then
array(ind) = i
ind = ind + 1
end if
end do
print *, array(1)
print *, array(2)
print *, array(3)
print *, array(4)
print *, array(5)
end program primes
function is_prime(n) result(ispr)
implicit none
integer :: c, i
integer, intent(in) :: n
logical :: ispr
c = 0
do i = 2, n
if (mod(i,2) == 0) then
c = c + 1
end if
end do
ispr = (c == 0)
end function is_prime
I don't know why but this is the output
9175178
6417360
5374044
6750309
7536745
Why does this happen and how to correct?
is_prime should be(n is the only divider of n besides 1 <=> c == 1)
function is_prime(n) result(ispr)
implicit none
integer :: c, i
integer, intent(in) :: n
logical :: ispr
c = 0
do i = 2, n
if (mod(n,i) == 0) then
c = c + 1
end if
end do
ispr = (c == 1)
end function is_prime
Could be optimezed by leaving the loop when c == 1 and i < n(after adding 1 to c)...
See on online fortran compiler
version with exit loop
While I am not familiar with modern Fortran, it looks to me as if function is_prime(n) result(ispr) is not working.
In the do loop in that function, you want a loop that tests thus:
is n divisible by 2?
is n divisible by 3?
is n divisible by 4?
is n divisible by 5?
and so on.
But, what it is actually doing is asking these:
is 2 divisible by 2?
is 3 divisible by 2?
is 4 divisible by 2?
is 5 divisible by 2?
and so on.
As a result, your counter will always have a non-zero value, and your function will always return false.
But, that's not the only problem. From your results, it looks like your Fortran implementation does not automatically initialize variables. Suppose I have statements like the following:
integer :: b
print *,b
What will be the result?
Remember, the names of variables represent locations in the computer's memory. If a variable is not initialized, it's value will be what was in the memory location before your program started to run. This value will not be related to your program.
I have 2 suggestions to fix the 2nd problem:
Prior to do i = 2, 10000, 1, have another loop that sets each value in array.
Set a values of each array (i) inside your do i = 2, 10000, 1 loop. One way to do this is to set one value when (is_prime(i) .eqv. .true.) is true and a different value when it is false.
This question already has answers here:
Does Fortran preserve the value of internal variables through function and subroutine calls?
(3 answers)
Closed 2 years ago.
I'm recently studying Fortran, and trying to make a program to check the prime number. The function works fine without any loop. It can give 1 when the given number is a prime number and 0 otherwise. However, it doesn't work properly when it is used in do while loop. In the range of 2 ~ 10, it is supposed to give 1 (for 2), 1(for 2), 0(for 4), 1(for 5), 0(for 6), etc. But, it keeps showing only 0. I'm pretty new to programming, so I'm not sure what I'm missing. I know there are many answers related to prime numbers, but I don't see any issue like this.
** Function checking prime numbers **
module prime_function
contains
integer function isPrime(inp_num)
implicit none
integer :: inp_num
integer :: i = 1
integer :: temp1 = 0
do while (i < inp_num)
i = i + 1
if(mod(inp_num, i) == 0) then
exit
end if
end do
if(inp_num == i) then
temp1 = 1
else
temp1 = 0
end if
isPrime = temp1
end function
end module
program fortran_q
use prime_function
implicit none
integer :: ii, a
a = isPrime(10)
print *, "10 is prime number, so the return : ", a
a = isPrime(11)
print *, "11 is prime number, so the return : ", a
ii = 1
do while (ii < 10)
ii = ii + 1
print *, isPrime(ii)
end do
end program
** Results **
10 is prime number, so the return : 0
11 is prime number, so the return : 1
0
0
0
0
0
0
0
0
0
You have a classic issue for people new to Fortran. The initialization of i and temp0 implies the SAVE attribute. When you call isPrime for the first time the values are set to 1 and 0. On the next invocation, the values of i and temp0 are set to whatever their previous values were when isPrime was last executed. The belong program fixes the issue.
module prime_function
implicit none
private
public isprime
contains
function isPrime(inp_num) result(res)
integer res
integer, intent(in) :: inp_num
integer i, temp1
i = 1
temp1 = 0
do while (i < inp_num)
i = i + 1
if (mod(inp_num, i) == 0) exit
end do
res = 0
if (inp_num == i) res = 1
end function
end module
program fortran_q
use prime_function
implicit none
integer :: ii, a
a = isPrime(10)
print *, "10 is prime number, so the return : ", a
a = isPrime(11)
print *, "11 is prime number, so the return : ", a
ii = 1
do while (ii < 10)
ii = ii + 1
print *, isPrime(ii)
end do
end program
I am passing two values to my Fortran program, I need to get the sum of those arguments and print it as result:
I have the program for reading arguments as follows:
PROGRAM Argtest
IMPLICIT NONE
integer*4 nargs,i
character arg*80
nargs = iargc()
do i = 0,nargs
call getarg(i, arg)
print '(a)', arg
end do
END
I am passing the values 10 and 20.
I tried like this:
PROGRAM Argtest
IMPLICIT NONE
integer:: nargs,i
character:: arg
integer:: num1
integer:: num2
integer:: result
nargs = iargc()
do i = 1,nargs
call getarg(i, arg)
!print *, arg
IF( i == 1) THEN
num1 = ichar(arg)
ELSE IF(i == 2) THEN
num2 = ichar(arg)
ELSE
end IF
end do
result = num1+num2
print *, num1
print*,num2
END
I need to print the answer as 30. But I am getting values 49 and 50 instead of getting 10 and 30. Please help me.
Here is a very simple version: It reads the arguments as strings, converts them into ints one after the other, and adds them all up.
PROGRAM Argtest
IMPLICIT NONE
integer*4 nargs,i
character arg*80
integer :: total, int_arg
nargs = iargc()
total = 0
do i = 1,nargs
call getarg(i, arg)
read(arg, *) int_arg
total = total + int_arg
end do
print *, "total is ", total
END
Note that I am starting from argument 1, not 0 (as that is your program name, and can't be converted into a number).
You have now updated your question: ichar converts a single character into the integer that corresponds to that character's ASCII code.
You need to use read(ch_num, '(I)') int_num to convert a string like "10" to the integer number 10.
I am making a program that converts a decimal integer into its binary representation. Here is my code:
program test
implicit none
integer, dimension(:), allocatable :: binary
integer :: decimalnum, i, initvalue
print*, "Enter decimal number to convert: "
read*,initvalue
decimalnum = initvalue
i = 0
do while (decimalnum > 0)
if (MOD(decimalnum,2)==0) then
binary(i) = 0 ! this is as far as the program executes up to
decimalnum = decimalnum / 2
i = i + 1
else if (MOD(decimalnum,2)==1) then
binary(i) = 1
decimalnum = (decimalnum -1) / 2
i = i + 1
end if
end do
end program test
At the marked point, it returns the error Segmentation fault and exits with code 139.
Why does this happen?
Thanks in advance.
Here's a simple way to convert an integer i to its binary representation:
write(*,'(b16)') i
As written, this won't write any leading 0s. If you want the leading 0s, try
write(*,'(b16.16)') i
Of course, the preceding code writes the binary representation to the default output unit but using Fortran's internal write capabilities I could just as easily write the bits to a character variable. For example:
character(len=16) :: bits
...
write(bits,'(b16.16)') i
writes the binary digits of i into the character variable bits.
Now, if what you really want is to create an array of integers each representing one bit of the binary representation, then something like this
integer, dimension(16) :: bitarray
...
bitarray = 0
...
do ix = 1,len(bits)
if (bits(ix:ix)=='1') bitarray(ix) = 1
end do
would probably work.
1) Your crash occurs because you had allocated only 1 element for the array binary(:), and the While loop presumably had moved on to i = 2, at which point your array is index out of bounds (crash).
2) Fortran has a number of intrinsic functions that deal with bits directly. For example,
a) Bit_Size(var) returns the number of bits in "var", so if you must use an allocatable, now you know the array size required in advance.
b) BTest(iVar, pos) returns .True. if the bit at pos in iVar is 1
For example, using the other declarations above,:
Integer :: n
n = Bit_Size(decimalnum)
If( Allocated(Binary) ) DeAllocate(Binary) ! good practice
Allocate( Binary(1:n) ) ! in general, should get IOStat, just in case
Binary(:) = 0
ForAll(i=1:n, Mask = BTest( decimalnum, i-1 ) ) ! remember, bit intrinsics index from 0
Binary(i) = 1
End ForAll
... this is a little more efficient compared to Do and While, and may help (a little) towards smp. The Where/EndWhere construct can be used also, but I find ForAll's a little more efficient.
c) IBits(var, pos, len) extracts the bits from var starting at pos for len number of bits, for example if you wanted to create an "explicit" binary representation, that might be one way to go.
etc etc
3) If you "really mean" convert DecimalNum to Bin, then you have (substantial) additional problems, if Dec also includes floating point Dec (i.e. Reals), since the bit representation of Reals is in terms of exponents. I will assume that is not the case, as the code/explanation for that is much much more involved.
Finally, in Fortran, Nums are usually "signed" Nums and the leading bit is used to determine +ve (0) or -ve (1). So if you were going in the "other" direction (Bin2Dec), then would prefer an additional arg (perhaps optional) that controls whether the result is signed or unsigned. If unsigned, then the output var will need to be "bigger" compared to the input var (e.g. if converting unsigned 1-byt int to Fortran int, then must use at least 2-byte int (i.e. input Integer(1) must be output to an Integer(2)) etc.
As per the comments, you need to have executed an allocate statement (or something which does an allocation for you under the covers) before you can define the binary array. The simplest form of allocation statement would look something like ALLOCATE(binary(10)), which would given the binary array 10 elements, using the default (it can be changed for that array using the allocate statement) starting array index of 1.
Where the size of the allocation is not easily known before working with an array there are two basic approaches:
Do two passes, the first pass of which simply counts how many elements are required, then the array is allocated, then the second pass actually does the assignment to the relevant elements.
Allocate the array to an initial size (which may be zero), the progressively grow the array as required.
There are trade-offs associated with the decision around the approach to use associated with the relative overheads of things like allocation and the evaluation of each test when counting.
In Fortran 90 (time to move on to at least Fortran 95!), growing an allocatable array is somewhat convoluted (allocate a temporary, copy data from original to temporary, deallocate original, allocate original to new size, copy data from temporary back to resized original, deallocate temporary). In Fortran 2003 this operation becomes trivial.
so this is probably awful form, and certainly bad runtime (it copies the array for every single bit), but here's what I came up with. It seems to work.
program test
implicit none
integer, dimension(:), allocatable :: binary
integer :: decimalnum, i, initvalue, curSize, curBit
print*, "Enter decimal number to convert: "
read*,initvalue
decimalnum = initvalue
i = 1
ALLOCATE ( binary(1) )
curSize = 1
DO WHILE (decimalnum > 0)
IF (i > curSize ) THEN
curSize = curSize * 2
CALL expandArray( curSize, i-1 )
END IF
IF (MOD(decimalnum,2)==0) then
binary(i) = 0 ! this is as far as the program executes up to
decimalnum = decimalnum / 2
i = i + 1
ELSE IF (MOD(decimalnum,2)==1) then
binary(i) = 1
decimalnum = (decimalnum -1) / 2
i = i + 1
END IF
end do
PRINT*, binary
CONTAINS
SUBROUTINE expandArray( newSize, oldSize )
IMPLICIT NONE
INTEGER, DIMENSION(:), ALLOCATABLE :: temp
INTEGER :: j, newSize, oldSize
ALLOCATE( temp(newSize) )
DO j=1,oldSize
temp(j) = binary(j)
END DO
DEALLOCATE (binary)
ALLOCATE( binary(newSize) )
DO j=1,oldSize
binary(j) = temp(j)
END DO
DO j=oldSize+1,newSize
binary(j) = 0
END DO
DEALLOCATE (temp)
END SUBROUTINE
END PROGRAM test