Related
This question already has answers here:
Fortran assignment on declaration and SAVE attribute gotcha
(2 answers)
Does Fortran preserve the value of internal variables through function and subroutine calls?
(3 answers)
Closed last year.
I am overriding the concatenation operator to allow real values to be concatenated with strings.
function concatenateRealSigFigsWithString( floatIn, str ) result(cat)
! Arguments
character( len=* ), intent(in) :: str
real(8), intent(in) :: floatIn(2)
! Returns
character( len=: ), allocatable :: cat
! Variables
logical :: remove_decimal = .False.
real(8) :: float
character( len=16 ) :: float_as_str, fmt
integer :: sigfig, i
float = floatIn(1)
sigfig = int(floatIn(2))
do i = sigfig, 1, -1
if (float < 10.0_8**i .and. float > 10.0_8**(i-1) ) then
fmt = '(F' // sigfig + 1 // '.' // sigfig - i // ')'
if (i == sigfig) remove_decimal = .True.
end if
end do
if ( float < 1 ) then
fmt = '(F' // sigfig + 2 // '.' // sigfig // ')'
end if
if (float < 0.1 ) then
fmt = '(ES' // sigfig + 5 // '.' // sigfig - 1 // ')'
end if
if ( float > 10.0_8**sigfig ) then
fmt = '(ES' // sigfig+6 // '.' // sigfig-1 // ')'
end if
write(float_as_str,fmt) float
if (remove_decimal) float_as_str = float_as_str(1:len_trim(float_as_str)-1)
cat = trim(float_as_str) // str
! cat = fmt
end function concatenateRealSigFigsWithString
The real value is a actually a list. The first element is the number and the second is the significant figures. Values between 0 and 10^(sigfig) are given in decimal form. Otherwise, the value is given in scientific form. The concatenation works for all values except those greater than 10^(sigfig). So the focus of the issue here is in the line fmt = '(ES' // sigfig+6 // '.' // sigfig-1 // ')'. For example, a value of 300,000 that should be written to 3 significant figures is formatted as 3.00E+0.
I can't figure out why the exponent is only showing one digit instead of 2, since it should be 3.00E+05.
I have tried specifying the width of exponent like fmt = '(ES' // sigfig+6 // '.' // sigfig-1 // 'E2)' but that just gives me 3.00E+00. I also tried expanding the width in case I did my math wrong and the exponent was getting cut off, but all that did was increase leading white space.
Any idea why the exponent is not showing the true value. Here is the code I used to test it.
program test
interface operator(//)
procedure :: concatenateRealSigFigsWithString, concatenateStringWithInteger
end interface
real(8) :: sigfig = 3
print *, [2.337e-7_8, sigfig] // " slugs"
print *, [2.337e-6_8, sigfig] // " slugs"
print *, [2.337e-5_8, sigfig] // " slugs"
print *, [2.337e-4_8, sigfig] // " slugs"
print *, [2.337e-3_8, sigfig] // " slugs"
print *, [2.337e-2_8, sigfig] // " slugs"
print *, [2.337e-1_8, sigfig] // " slugs"
print *, [2.337e0_8, sigfig] // " slugs"
print *, [2.337e01_8, sigfig] // " slugs"
print *, [2.337e02_8, sigfig] // " slugs"
print *, [2.337e03_8, sigfig] // " slugs"
print *, [2.337e4_8, sigfig] // " slugs"
print *, [2.337e5_8, sigfig] // " slugs"
print *, [2.337e6_8, sigfig] // " slugs"
print *, [2.337e7_8, sigfig] // " slugs"
print *, [2.337e8_8, sigfig] // " slugs"
print *, [2.337e9_8, sigfig] // " slugs"
print *, [2.337e10_8, sigfig] // " slugs"
contains
function concatenateStringWithInteger(str,int) result(cat)
! Arguments
character( len=* ), intent(in) :: str
integer, intent(in) :: int
! Returns
character( len=: ), allocatable :: cat
! Variables
character( len=9 ) :: int_as_str
write(int_as_str,'(I0)') int
cat = str // trim(int_as_str)
end function concatenateStringWithInteger
function concatenateRealSigFigsWithString( floatIn, str ) result(cat)
! Arguments
character( len=* ), intent(in) :: str
real(8), intent(in) :: floatIn(2)
! Returns
character( len=: ), allocatable :: cat
! Variables
logical :: remove_decimal = .False.
real(8) :: float
character( len=16 ) :: float_as_str, fmt
integer :: sigfig, i
float = floatIn(1)
sigfig = int(floatIn(2))
do i = sigfig, 1, -1
if (float < 10.0_8**i .and. float > 10.0_8**(i-1) ) then
fmt = '(F' // sigfig + 1 // '.' // sigfig - i // ')'
if (i == sigfig) remove_decimal = .True.
end if
end do
if ( float < 1 ) then
fmt = '(F' // sigfig + 2 // '.' // sigfig // ')'
end if
if (float < 0.1 ) then
fmt = '(ES' // sigfig + 5 // '.' // sigfig - 1 // ')'
end if
if ( float > 10.0_8**sigfig ) then
fmt = '(ES' // sigfig+6 // '.' // sigfig-1 // ')'
end if
write(float_as_str,fmt) float
if (remove_decimal) float_as_str = float_as_str(1:len_trim(float_as_str)-1)
cat = trim(float_as_str) // str
! cat = fmt
end function concatenateRealSigFigsWithString
end program test
I'm trying to read a four column data file. However, because I'm having so much trouble, I'm just trying to read a single column of integers. Here is my code:
program RFF_Simple
implicit none
! Variables
character(len = 100):: line_in
character(len = :), allocatable :: filename
integer, dimension(:), allocatable :: weight,numbers
real, dimension(:), allocatable :: fm, fc
integer :: iostat_1, iostat_2
integer :: lun, length, index
! Body of RFF_Simple
! filename = 'data.txt'
filename = 'data_test.txt'
iostat_1 = 0
iostat_2 = 0
length = 0
open(newunit = lun, file = filename, status = 'old', iostat = iostat_1)
!Count how many lines are in the file (length)
if (iostat_1 == 0) then
do while(iostat_2 == 0)
read(lun, '(a)', iostat = iostat_2) line_in
if (iostat_2 == 0) then
length= length + 1
endif
enddo
endif
rewind(lun)
allocate(numbers(length)) !Allocate arrays to have same length as number of lines
iostat_1 = 0 !Reset
iostat_2 = 0
index = 1 !This whole thing is confusing so I don't know whether starting from 1 or 0 is better....
if (iostat_1 == 0) then
do while(iostat_2 == 0)
if(iostat_2 == 0) then
read(lun,*, iostat = iostat_2) numbers(index) !This crashes the program (Severe 408)
index = index + 1
endif
enddo
endif
write(*,*) 'Press Enter to Exit'
read(*,*)
end program RFF_Simple
The code compiles no problem, but running it yields the following: http://imgur.com/a/6ciJS
Yes I that is a print screen.
I don't even know where to start with this one.
The problem here is that you increment index after each successful read. After the last successful read we have index=length. You then add 1 to index and then attempt to read numbers(length+1) which results in a bounds violation. Rather than looping with a do while you can just use a regular do loop since we know the number of lines to read.
do index = 1, length
read(lun,*) numbers(index)
enddo
You could also test whether index is greater than length and bail out of the loop.
I have a few 110-element vectors. They sometimes have a value from 0 to 9, but their default value is -1. I'd like to print a blank if a cell's value is -1; print their value otherwise.
I'm printing several things in an output line so I can't use an if with two writes. Passing the values to a character vector worked but I can't help but think there must be a better way.
My attempt:
program integer_print_blank_test
implicit none
integer, dimension(9) :: longint
character(len=3), dimension(9) :: longchar
integer :: i, j
do i = 0, 2
write(*,*) (longint(3*i+j), j = 1, 3)
end do
longint = -1
longint(1) = 1
longint(4) = 3
longint(9) = 7
write(*,*) "longint"
do i = 0, 2
write(*,*) (longint(3*i+j), j = 1, 3)
end do
do i = 1, 9
write(longchar(i),"(I3)") longint(i)
end do
write(*,*) "longchar"
do i = 0, 2
write(*,*) (longchar(3*i+j), j = 1, 3)
end do
write(*,*) "only positives in longchar"
longchar = " "
do i = 1, 9
if (longint(i) > -1) then
write(longchar(i),"(I3)") longint(i)
end if
end do
do i = 0, 2
write(*,*) (longchar(3*i+j), j = 1, 3)
end do
end program integer_print_blank_test
You might think this is a better way. Define a function such as
ELEMENTAL FUNCTION borf(int) RESULT(str)
INTEGER, INTENT(in) :: int
CHARACTER(len=2) :: str
str = ' '
IF (int>-1) WRITE(str,'(i2)') int
END FUNCTION borf
and use it like this
WRITE(*,*) borf(longint)
I'm learning how to programming with fortran90 and i need receive data from a txt file by the command prompt (something like that:
program.exe"<"data.txt).
at the Input txt file I'll always have a single line with at least 6 numbers till infinity.
if the data was wrote line by line it runs fine but as single line I'm receiving the error: "traceback:not available,compile with - ftrace=frame or - ftrace=full fortran runtime error:end file"
*note: i'm using Force fortran 2.0
here is example of data:
0 1 0.001 5 3 1 0 -9 3
edit: just clarifying: the code is working fine itself except for the read statement, which is a simple "read*,". I want know how To read a entire line from a txt once the entrance will be made by the promt command with stream direction.
( you can see more about that here: https://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/redirection.mspx?mfr=true).
there is no need to read the code, i've posted it just for knowledge.
I'm sorry about the whole inconvenience.
here is the code so far:
program bissecao
implicit none
integer::cont,int,e,k,intc,t1,t2,t3
doubleprecision::ii,is,pre,prec,erro,somaa,somab,xn
doubleprecision,dimension(:),allocatable::co
t1=0
t2=0
t3=0
! print*,"insira um limite inf da funcao"
read*,ii
!print*,"insira o limite superior da func"
read*,is
! print*,"insira a precisÆo admissivel"
read*,pre
if (erro<=0) then !elimina criterio de parada negativo ou zero
Print*,"erro"
go to 100
end if
!print*,"insira a qtd iteracoes admissiveis"
read*,int
!print*,"insira o grau da f(x)"
read*,e
if (e<=0) then ! elimina expoente negativo
e=(e**2)**(0.5)
end if
allocate(co(e+1))
!print*, "insira os coeficientes na ordem:&
! &c1x^n+...+(cn-1)x^1+cnx^0"
read(*,*)(co(k),k=e+1,1,-1)
somab=2*pre
intc=0
do while (intc<int.and.(somab**2)**0.5>pre.and.((is-ii)**2)**0.5>pre)
somab=0
somaa=0
xn =(ii+is)/2
do k=1,e+1,1
if (ii /=0) then
somaa=ii**(k-1)*co(k)+somaa
else
somaa=co(1)
end if
! print*,"somaa",k,"=",somaa
end do
do k=1,(e+1),1
if (xn/=0) then
somab=xn**(k-1)*co(k)+somab
else
somab=co(1)
end if
!print*,"somab",k,"=",somab
end do
if ((somaa*somab)<0) then
is=xn
else if((somaa*somab)>0)then
ii=xn
else if ((somaa*somab)==0) then
xn=(ii+is)/2
go to 100
end if
intc =intc+1
prec=is-ii
if ((((is-ii)**2)**.5)< pre) then
t3=1
end if
if (((somab**2)**.5)< pre) then
t2=1.
end if
if (intc>=int) then
t1=1
end if
end do
somab=0
xn=(ii+is)/2
do k=1,(e+1),1
if (xn/=0) then
somab=xn**(k-1)*co(k)+somab
else
somab=co(1)
end if
end do
100 write(*,'(A,F20.15,A,F20.15,A,A,F20.15,A,F20.15,A,I2)'),"I:[",ii,",",is,"]","raiz:",xn,"Fraiz:",somab,"Iteracoes:",intc
end program !----------------------------------------------------------------------------
In your program, you are using the "list-directed input" (i.e., read *, or read(*,*))
read *, ii
read *, is
read *, pre
read *, int
read *, e
read *, ( co( k ), k = e+1, 1, -1 )
which means that the program goes to the next line of the data file after each read statement (by neglecting any remaining data in the same line). So, the program works if the data file (say "multi.dat") consists of separate lines (as suggested by OP):
0
1
0.001
5
3
1 0 -9 3
But now you are trying to read an input file containing only a single line (say "single.dat")
0 1 0.001 5 3 1 0 -9 3
In this case, we need to read all the values with a single read statement (if list-directed input is to be used).
A subtle point here is that the range of array co depends on e, which also needs to be read by the same read statement. A workaround might be to just pre-allocate co with a sufficiently large number of elements (say 100) and read the data in a single line, e.g.,
integer :: k
allocate( co( 100 ) )
read *, ii, is, pre, int, e, ( co( k ), k = e+1, 1, -1 )
For completeness, here is a test program where you can choose method = 1 or 2 to read "multi.dat" or "single.dat".
program main
implicit none
integer :: int, e, k, method
double precision :: ii, is, pre
double precision, allocatable :: co(:)
allocate( co( 1000 ) )
method = 1 !! 1:multi-line-data, 2:single-line-data
if ( method == 1 ) then
call system( "cat multi.dat" )
read*, ii
read*, is
read*, pre
read*, int
read*, e
read*, ( co( k ), k = e+1, 1, -1 )
else
call system( "cat single.dat" )
read*, ii, is, pre, int, e, ( co( k ), k = e+1, 1, -1 )
endif
print *, "Input data obtained:"
print *, "ii = ", ii
print *, "is = ", is
print *, "pre = ", pre
print *, "int = ", int
print *, "e = ", e
do k = 1, e+1
print *, "co(", k, ") = ", co( k )
enddo
end program
You can pass the input file from standard input as
./a.out < multi.dat (for method=1)
./a.out < single.dat (for method=2)
Please note that "multi.dat" can also be read directly by using "<".
I have put a check in error made in the input as:
integer :: lsp
chksp:do
write(*,*) "#Enter Number"
read(*,*,iostat=istat)lsp
if (istat==0) then
exit chksp
else
write(*,*)"Number can only be integer. Re-enter!"
end if
end do chksp
The problem is, it can detect error if a character value in enteres, instead of a numeric value; but it cannot detect error, if a real value is entered, instead of a integer.
Any way to force it detect integer only?
NB: May be problem with ifort; gfortran is happy with the code.
You can specify the format to request an integer:
program enter_int
implicit none
integer :: ierror, intVal
do
write(*,*) "Enter an integer number"
read(*,'(i10)',iostat=ierror) intval
if ( ierror == 0 ) then
exit
endif
write(*,*) 'An error occured - please try again'
enddo
write(*,*) 'I got: ', intVal
end program
Then, providing a float fails.
Something like the following?
ian#ian-pc:~/test/stackoverflow$ cat read.f90
Program readit
Integer :: val
Integer :: iostat
val = -9999
Do
Read( *, '( i20 )', iostat = iostat ) val
If( iostat == 0 ) Then
Write( *, * ) 'val = ', val
Else
Write( *, * ) 'oh dear!!'
End If
End Do
End Program readit
ian#ian-pc:~/test/stackoverflow$ nagfor -o read read.f90
NAG Fortran Compiler Release 5.3.1(907)
[NAG Fortran Compiler normal termination]
ian#ian-pc:~/test/stackoverflow$ ./read
10
val = 10
10.0
oh dear!!
safs
oh dear!!
123dfs23
oh dear!!
^C
ian#ian-pc:~/test/stackoverflow$ gfortran -o read read.f90
ian#ian-pc:~/test/stackoverflow$ ./read
10
val = 10
10.0
oh dear!!
dsfs
oh dear!!
^C
ian#ian-pc:~/test/stackoverflow$