Skip to content
GitLab
Projects Groups Snippets
  • /
  • Help
    • Help
    • Support
    • Community forum
    • Submit feedback
    • Contribute to GitLab
  • Sign in
  • FORCES FORCES
  • Project information
    • Project information
    • Activity
    • Labels
    • Members
  • Repository
    • Repository
    • Files
    • Commits
    • Branches
    • Tags
    • Contributors
    • Graph
    • Compare
  • Issues 11
    • Issues 11
    • List
    • Boards
    • Service Desk
    • Milestones
  • Merge requests 3
    • Merge requests 3
  • CI/CD
    • CI/CD
    • Pipelines
    • Jobs
    • Schedules
  • Deployments
    • Deployments
    • Environments
    • Releases
  • Packages and registries
    • Packages and registries
    • Container Registry
  • Monitor
    • Monitor
    • Incidents
  • Analytics
    • Analytics
    • Value stream
    • CI/CD
    • Repository
  • Wiki
    • Wiki
  • Snippets
    • Snippets
  • Activity
  • Graph
  • Create a new issue
  • Jobs
  • Commits
  • Issue Boards
Collapse sidebar
  • CHSCHS
  • FORCESFORCES
  • Issues
  • #8
Closed
Open
Issue created Jun 11, 2020 by Robert Schweppe@ottorOwner

Add a precision argument to mo_utils comparison operators

I wrote a little program, simulating the behaviour of == and eq from mu_utils.f90 for a set of real numbers -huge(dp), -1.0, 0.0, 1.0, huge(dp) altered by -1.0e-9_dp, -epsilon(dp), epsilon(dp), 1.0e-9_dp.

module mo_kind
  implicit none
contains
  function eq(a, b) result(boolean)
   use iso_fortran_env, only: sp=>real32, dp=>real64, qp=>real128
   use iso_fortran_env, only: i1=>int8, i2=>int16, i4=>int32, i8=>int64
   
   real(dp), intent(in) :: a, b
   logical :: boolean
   
   if ((epsilon(1.0_dp) * abs(b) - abs(a - b)) < 0.0_dp) then
      boolean = .false.
   else
      boolean = .true.
   end if
 end function eq
end module mo_kind

 program float_comparison
   use iso_fortran_env, only: sp=>real32, dp=>real64, qp=>real128
   use iso_fortran_env, only: i1=>int8, i2=>int16, i4=>int32, i8=>int64
   use mo_kind, only: eq

   implicit none
   real(dp), parameter :: as(5) = [huge(1.0_dp) * (-1.0_dp) + 10.0_dp, -1.0_dp, 0.0_dp, 1.0_dp, huge(1.0_dp) - 10.0_dp]
   real(dp), parameter :: mods(4) = [-1.0e-9_dp, -epsilon(1.0_dp), epsilon(1.0_dp), 1.0e-9_dp]
   real(dp) :: a, b
   integer(i4) :: i, j
   logical :: boolean_1, boolean_2

   do i=1, size(as)
      a = as(i)
      do j=1, size(mods)
         b = a + mods(j)
         print*, 'mo_utils: eq(', a, ', ', b, ') = ', eq(a,b)
         print*, 'mo_utils: eq(', b, ', ', a, ') = ', eq(b,a)
         print*, '==      :   ', a, '== ', b, ' = ', a == b
      end do
   end do
 end program float_comparison

which generates this output (GFortran9.3 on MacOSX)

 mo_utils: eq(  -1.7976931348623157E+308 ,   -1.7976931348623157E+308 ) =  T
 mo_utils: eq(  -1.7976931348623157E+308 ,   -1.7976931348623157E+308 ) =  T
 ==      :     -1.7976931348623157E+308 ==   -1.7976931348623157E+308  =  T
 mo_utils: eq(  -1.7976931348623157E+308 ,   -1.7976931348623157E+308 ) =  T
 mo_utils: eq(  -1.7976931348623157E+308 ,   -1.7976931348623157E+308 ) =  T
 ==      :     -1.7976931348623157E+308 ==   -1.7976931348623157E+308  =  T
 mo_utils: eq(  -1.7976931348623157E+308 ,   -1.7976931348623157E+308 ) =  T
 mo_utils: eq(  -1.7976931348623157E+308 ,   -1.7976931348623157E+308 ) =  T
 ==      :     -1.7976931348623157E+308 ==   -1.7976931348623157E+308  =  T
 mo_utils: eq(  -1.7976931348623157E+308 ,   -1.7976931348623157E+308 ) =  T
 mo_utils: eq(  -1.7976931348623157E+308 ,   -1.7976931348623157E+308 ) =  T
 ==      :     -1.7976931348623157E+308 ==   -1.7976931348623157E+308  =  T
 mo_utils: eq(  -1.0000000000000000      ,   -1.0000000010000001      ) =  F
 mo_utils: eq(  -1.0000000010000001      ,   -1.0000000000000000      ) =  F
 ==      :     -1.0000000000000000      ==   -1.0000000010000001       =  F
 mo_utils: eq(  -1.0000000000000000      ,   -1.0000000000000002      ) =  T
 mo_utils: eq(  -1.0000000000000002      ,   -1.0000000000000000      ) =  T
 ==      :     -1.0000000000000000      ==   -1.0000000000000002       =  F
 mo_utils: eq(  -1.0000000000000000      ,  -0.99999999999999978      ) =  F
 mo_utils: eq( -0.99999999999999978      ,   -1.0000000000000000      ) =  T
 ==      :     -1.0000000000000000      ==  -0.99999999999999978       =  F
 mo_utils: eq(  -1.0000000000000000      ,  -0.99999999900000003      ) =  F
 mo_utils: eq( -0.99999999900000003      ,   -1.0000000000000000      ) =  F
 ==      :     -1.0000000000000000      ==  -0.99999999900000003       =  F
 mo_utils: eq(   0.0000000000000000      ,   -1.0000000000000001E-009 ) =  F
 mo_utils: eq(  -1.0000000000000001E-009 ,    0.0000000000000000      ) =  F
 ==      :      0.0000000000000000      ==   -1.0000000000000001E-009  =  F
 mo_utils: eq(   0.0000000000000000      ,   -2.2204460492503131E-016 ) =  F
 mo_utils: eq(  -2.2204460492503131E-016 ,    0.0000000000000000      ) =  F
 ==      :      0.0000000000000000      ==   -2.2204460492503131E-016  =  F
 mo_utils: eq(   0.0000000000000000      ,    2.2204460492503131E-016 ) =  F
 mo_utils: eq(   2.2204460492503131E-016 ,    0.0000000000000000      ) =  F
 ==      :      0.0000000000000000      ==    2.2204460492503131E-016  =  F
 mo_utils: eq(   0.0000000000000000      ,    1.0000000000000001E-009 ) =  F
 mo_utils: eq(   1.0000000000000001E-009 ,    0.0000000000000000      ) =  F
 ==      :      0.0000000000000000      ==    1.0000000000000001E-009  =  F
 mo_utils: eq(   1.0000000000000000      ,   0.99999999900000003      ) =  F
 mo_utils: eq(  0.99999999900000003      ,    1.0000000000000000      ) =  F
 ==      :      1.0000000000000000      ==   0.99999999900000003       =  F
 mo_utils: eq(   1.0000000000000000      ,   0.99999999999999978      ) =  F
 mo_utils: eq(  0.99999999999999978      ,    1.0000000000000000      ) =  T
 ==      :      1.0000000000000000      ==   0.99999999999999978       =  F
 mo_utils: eq(   1.0000000000000000      ,    1.0000000000000002      ) =  T
 mo_utils: eq(   1.0000000000000002      ,    1.0000000000000000      ) =  T
 ==      :      1.0000000000000000      ==    1.0000000000000002       =  F
 mo_utils: eq(   1.0000000000000000      ,    1.0000000010000001      ) =  F
 mo_utils: eq(   1.0000000010000001      ,    1.0000000000000000      ) =  F
 ==      :      1.0000000000000000      ==    1.0000000010000001       =  F
 mo_utils: eq(   1.7976931348623157E+308 ,    1.7976931348623157E+308 ) =  T
 mo_utils: eq(   1.7976931348623157E+308 ,    1.7976931348623157E+308 ) =  T
 ==      :      1.7976931348623157E+308 ==    1.7976931348623157E+308  =  T
 mo_utils: eq(   1.7976931348623157E+308 ,    1.7976931348623157E+308 ) =  T
 mo_utils: eq(   1.7976931348623157E+308 ,    1.7976931348623157E+308 ) =  T
 ==      :      1.7976931348623157E+308 ==    1.7976931348623157E+308  =  T
 mo_utils: eq(   1.7976931348623157E+308 ,    1.7976931348623157E+308 ) =  T
 mo_utils: eq(   1.7976931348623157E+308 ,    1.7976931348623157E+308 ) =  T
 ==      :      1.7976931348623157E+308 ==    1.7976931348623157E+308  =  T
 mo_utils: eq(   1.7976931348623157E+308 ,    1.7976931348623157E+308 ) =  T
 mo_utils: eq(   1.7976931348623157E+308 ,    1.7976931348623157E+308 ) =  T
 ==      :      1.7976931348623157E+308 ==    1.7976931348623157E+308  =  T

I learned some things:

  1. a == a is always True, trivial but important
  2. eq(a, a + epsilon) != eq(a + epsilon, a) at least for the (edge) cases 1.0_dp and -1.0_dp
  3. a == a + 1e-9 is True when a tends towards huge(dp)

I think this leads to the following:

  1. the usage of eq from mo_utils.f90 is not safe in all circumstances
  2. for the common case, where you init some value (e.g. a = nodata_dp) and later check for it with if (a == nodata_dp), it is okay to use == instead of eq. This of course does not hold true, if you read values a from a file because of precision loss.
  3. We need to improve the eq functions in mo_utils.f90 to check for absolute or relative tolerance (defaulting to epsilon in order to keep backwards compatibility. So something like abs(a - b) <= (atol + rtol * absolute(b)) (taken from numpy.allclose).

What do you think @muellese , @thober, @shresthp , @kaluza?

Edited Jan 11, 2022 by Sebastian Müller
Assignee
Assign to
Time tracking