Does Fortran store values in an array differently than a non-array declared variable?

73 views Asked by At
program analysis
        implicit none
        
        integer i, j, k, l
        double precision a, b, c, d, e
        integer binb(1:20),binc(1:20),bind1(1:20),bine(1:20)
        real lower(1:20),upper(1:20), next
    
        
    
        character(100) event
        
    
        upper(1)=-2.7
        lower(1)=-3.0
        binb(1:20)=0
        binc(1:20)=0
        bind1(1:20)=0
        bine(1:20)=0
        
        
        next=.3
        
    
    
            do l=2,20
                lower(l)=lower(l-1)+next
                upper(l)=upper(l-1)+next
            end do

        
            
        open(unit = 7, file="zpc_initial_momenta.dat")
            
            
            do k=1, 10
                read(7,'(A)') event
                do j=1,4000
                read(7,*) a, b, c, d, e
                
            do i=1, 20
                if(b>=lower(i) .and. b<upper(i)) then
            binb(i)=binb(i)+1
                end if
                if(c>=lower(i) .and. c<upper(i)) then 
            binc(i)=binc(i)+1
                end if
                if(d>=lower(i) .and. d<upper(i)) then
            bind1(i)=bind1(i)+1
                end if
                if(e>=lower(i) .and. e<upper(i)) then 
            bine(i)=bine(i)+1
                end if
            
            
            end do
            end do
            end do
            
            
                
                
        close(7)
        
            
        
        open(unit = 8, file="outputanalysis2.dat")
            Write(8,*) 'The bins in each column are as follows:'
            Write(8,*) 'FIRST COLUMN (MOMENTUM IN X DIRECTION)'
            write(8,*) binb(1:20)
            write(8,*) 'THE TOTAL COUNT IS', SUM(binb)
            Write(8,*) 'SECOND COLUMN (MOMENTUM IN Y DIRECTION)'
            write(8,*) binc(1:20)
            write(8,*) 'THE TOTAL COUNT IS', SUM(binc)
            Write(8,*) 'THIRD COLUMN (MOMENTUM IN Z DIRECTION)'
            write(8,*) bind1(1:20)
            write(8,*) 'THE TOTAL COUNT IS', SUM(bind1)
            Write(8,*) 'FOURTH COLUMN (KINETIC ENERGY)'
            write(8,*) bine(1:20)
            write(8,*) 'THE TOTAL COUNT IS', SUM(bine)
            
            
        close(8)
        
        end program
                

I have an issue that I'm half convinced is due to Fortran's base 2 round-off error, but I'm not entirely sure: In my if statements designed to count data for different ranges, I have an array for the upper and lower bound of each bin. I had a previous program that did not use arrays but was incredibly lengthy. In my array program, when executing the program, some bins are off by a max of 3 counts (not all of them, and some of them are off by less than that or none at all.) I did some further digging and compared it to a declared non-array variable equal to one of the bin's upper and lower bounds, and printed it out. For example: say, bin number 5, whose bin length is from (-1.8 to -1.5) for which it counts values in this range. When I used the non-array upper and lower bound to see if there was a change in the bin count, there was: it was 897, which is the correct bin count (found in the previous nonarray program.) However, when I use arrays to define each upper and lower limit, putting them into the if statement (as seen in this program) the count is 900. To see what was going on, I then had my program print out the nonarray variable equal to -1.8. When executed the program prints out: -1.79999995. When I had my program print out the array lower bound equivalent (lower(5)), it prints out -1.80000019. Can someone explain to me what is going on here, and if that is the reason the counts are off slightly for my array program? Is there a way to fix this? Thank you!

Edit: I think the best way to reproduce this is with a test program:

program help
    implicit none
    
    integer i
    real lower(1:5),next,nonarraylower2
    
    
    
    lower(1)=-2.1
    nonarraylower2=-0.9
    next=0.3
    
    do i=2,5
        lower(i)=lower(i-1)+next
    end do
    
    write(*,*) lower(5),nonarraylower2
    
    end program

If we write lower(2) and set nonarraylower2 equal to the same value that should be there according to the do loop, the values print to be the same. However, do lower(5) and set nonarraylower2 equal to what should be the same value (-.9) and have it print out both and you see there begins to show a difference. I should clarify: the data file I am pulling from has numbers small enough to where these minute changes in decimal places may matter. For example, here are some data points I am working with:

21  0.1314E+00 -0.1232E+01  0.6258E+00  0.1388E+01
    21 -0.3478E+00 -0.1268E+01 -0.3834E+00  0.1370E+01
    21  0.2138E+00  0.1995E+01 -0.9115E-01  0.2009E+01
    21  0.8936E+00 -0.5758E-01  0.7773E+00  0.1186E+01
    21 -0.5949E+00 -0.1999E+00  0.3787E+00  0.7330E+00

I hope my issue is clearer. When I originally built a program without using arrays to read each column and count how many belong to different ranges (defined by discrete bins), the count can sometimes be off by 5 maximum of my new program's output counts (it isn't consistent and sometimes I get the same counts. It seems the error is between 1 and 5 lost or extra counts.) What is going on here, and is there a way to fix it?

1

There are 1 answers

7
Deb S. B. On BEST ANSWER

I could not reproduce your error as I do not have the data, but your code should be fine unless you need high precision. I suggest you use real*8 for all float variables and parameters and assign values with the same precision: like next=0.3d0. I have uploaded a minimal code that worked for me. Hope that helps.

    program analysis
    implicit none
    
    integer :: i, j, k, l
    real*8 :: a, b, c, d, e
    integer :: binb(1:20),binc(1:20),bind1(1:20),bine(1:20)
    real*8 :: lower(1:20),upper(1:20), next

    integer :: bincheck


    upper(1)=-2.7d0
    lower(1)=-3.0d0
    binb(1:20)=0   
    
    next=0.3d0
    


        do l=2,20
            lower(l)=lower(l-1)+next
            upper(l)=upper(l-1)+next
        end do

    
        
    open(unit = 100, file="zpc_initial_momenta.dat")   
    do j=1,300
    read(100,*) b
            
    do i=1, 20
    if(b>=lower(i) .and. b<upper(i)) then
    binb(i)=binb(i)+1
    end if
        
    end do
    end do

    close(100)
      
!-------- check --------

    do i=1,20
    write(*,*) binb(i)
    enddo  
   
    open(unit = 100, file="zpc_initial_momenta.dat") 
    bincheck = 0
    do i=1,300
    read(100,*) b
    if(b>=lower(1) .and. b<upper(1)) then
    bincheck = bincheck +1
    end if
    enddo

    write(*,*) 'values in bin 1', bincheck


        
            
            
    close(100)
    
    end program

EDIT: example to check if values change when saved in arrays:

    program analysis
    implicit none
    
    integer :: i
    real*8 :: a
    real*8 :: alist(1:20)

    a=-1.79999995d0

    write(*,*) 'nonarray', a

    do i=1,20
    alist(i)=a
    write(*,*) 'array', i, alist(i)
    enddo

    end program

It gives me

 nonarray  -1.7999999499999999     
 array           1  -1.7999999499999999     
 array           2  -1.7999999499999999     
 array           3  -1.7999999499999999     
 array           4  -1.7999999499999999     
 array           5  -1.7999999499999999     
 array           6  -1.7999999499999999 
---- and so on -----

EDIT 2: modified program help

    program help
    implicit none
    
    integer i
    real*8 :: lower(1:5),next,nonarraylower2
    
    lower(1)=-2.1d0
    nonarraylower2=-0.9d0
    next=0.3d0
    
    do i=2,5
        lower(i)=lower(i-1)+next
    end do
    
    write(*,*) lower(5),nonarraylower2
    
    end program