arraysfortrangfortranfortran90

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


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?


Solution

  • 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