C Library Functions  - compare_float (3)

NAME

compare_float(3f) - [M_framework__approx] compare floating point values with adjustable tolerance. (LICENSE:PD)

CONTENTS

Synopsis
Description
Details
Options
Result
Operators
Examples

SYNOPSIS

result = compare_float( x, y,ulp = SCALING_VALUE)

     elemental function (x,y,ulp)
     real(kind=KIND),intent(in) :: x,y
     real|integer,intent(in),optional :: ulp

Additional convenience operators:

       X.equalto.Y
       X.lessthan.Y
       X.greaterthan.Y

Developer procedure (Do not use in production):

      change_default_ulp(ulp)

DESCRIPTION

compare_float(3f) is a function for comparing floating point numbers within an automatically adjusted tolerance.

The test performed is

       abs( x - y ) < ( ulp * spacing( max(abs(x),abs(y)) ) )

where ULP is a user-selected scaling factor that defaults to 1. The default is intentionally low so that default behavior is close to that of the default operators. Setting it to zero(0.0) essentially causes no values to compare equal.

If the result is .TRUE., the numbers are considered equal. Both single and double precision scalar and array values can be compared, as the function is elemental.

By definition of an elemental function the returned data entity is the same shape as the input array size or scalar if all values are scalar.

It can be useful to empirically test your code for numeric sensitivities by changing the value of the ULP scaling factor and noting any result changes.

As a convenience relational operators .EqualTo., .GreaterThan., and .LessThan. are provided. Note the comparisons return .TRUE> if the difference between the two values is .lt., .ge., and .le. . The algorithm for each operator is shown in the following OPERATORS section.

The default ULP value is 1.0. A procedure is available to change the default but it should only be used for examining code behavior during development, as it changes the default for calls from all procedures (even those in other modules or procedures).

      call default_ulp(ulp=VALUE)

DETAILS

It is generally acknowledged that real numbers should not be compared directly but within some tolerance. However, the magnitude of an appropriate tolerance value will vary depending on the magnitudes of the numbers being compared and the precision of the computing environment.

The Fortran standard does not specify functions or operators specifically for comparing float values, but leaves some latitude in how the compilers address floating point comparisons. It does specify functions that return platform-specific values useful in applying different methods to the problem such as

    + epsilon(3f)       - Epsilon function
    + nearest(3f)       - Nearest representable number
    + spacing(3f)       - Smallest distance between two numbers of a given type
    + rrspacing(3f)     - Reciprocal of the relative spacing of a numeric type

and in some cases

    + scale(3f)         - Scale a real value by a whole power of the radix
    + digits(3f)        - Significant digits in the numeric model
    + exponent(3f)      - Exponent of floating-point number
    + fraction(3f)      - Fractional part of the model representation
    + huge(3f)          - Largest number of a type and kind
    + maxexponent(3f)   - Maximum exponent of a real kind
    + minexponent(3f)   - Minimum exponent of a real kind
    + precision(3f)     - Decimal precision of a real kind
    + radix(3f)         - Base of a numeric model
    + range(3f)         - Decimal exponent range of a numeric kind
    + set_exponent(3f)  - real value with specified exponent
    + tiny(3f)          - Smallest positive number of a real kind

Books have been written on the behavior of floating point math.

As is used here, a commonly used simple floating point comparison algorithm is

       if(abs(x < y) < (ulp * spacing(max(abs(x),abs(y))))) then
         :
       endif

where the intrinsic function SPACING(3f) determines the distance between the argument X and the nearest adjacent representable number of the same type and ULP is an optional user-supplied scaling factor.

OPTIONS

x,y Two congruent floating point values to compare.
ulp The ULP ("unit in the last place") scaling value allows for users to control the scaling of the value returned by SPACING(3f) in order to relax or tighten what is considered "equal". That is, the ULP value can be used to scale the comparison based on knowledge of the "numerical quality" of the values being used in the comparison.

The value should be positive. The absolute value of the value is taken if it is negative.

The default ULP scaling value is 1.0.

The value may be of type integer or real.

A 0.5 ULP maximum error is the best you could hope for, since this corresponds to always rounding to the nearest representable floating point number.

RESULT

The return value is a logical value indicating whether the inputs are equal to within the requested precision.

OPERATORS

Additional operators based on compare_float(3f) are included:
X.equalto.Y
  If the result is .TRUE., the numbers are considered equal. The test performed is
               abs( x - y ) < spacing( max(abs(x),abs(y)) )

X.greaterthan.Y
  If the result is .TRUE., x is considered greater than y. The result is a logical value indicating whether the operand x is greater than y by more than the spacing between representable floating point numbers.

The test performed is

                  ( x - y ) >= SPACING( MAX(ABS(x),ABS(y)) )

X.lessthan.Y
  Test if one operand is less than another. The result is a logical value indicating whether the operand x is less than y by more than the spacing between representable floating point numbers.

The test performed is

                 ( y - x ) >= SPACING( MAX(ABS(x),ABS(y)) )

If the result is .TRUE., x is considered less than y.

EXAMPLES

Sample programs:

   program demo_compare_float
   use,intrinsic :: iso_fortran_env, only : int8, int16, int32, int64
   use,intrinsic :: iso_fortran_env, only : real32, real64, real128
   use,intrinsic :: iso_fortran_env, only : error_unit,output_unit
   use M_framework__approx,          only : compare_float
   use M_framework__approx,          only : &
   & operator(.equalto.), operator(.greaterthan.), operator(.lessthan.)
   implicit none
   integer,parameter       :: wp=int32
   integer                 :: i
   character(len=80),save  :: line=’10*0.1’
   real(kind=wp)           :: a(10), x, y, ulp
      write(*,*)’is 10*0.1 == 1.0?’
      ! sum up 0.1 ten times hopefully in a manner compiler does not
      ! optimize it and in the process make it equal
      a=0.1_wp
      read(line,*)a
      x=sum(a)
      y=1.0_wp
      write(*, *)merge(’    EQUAL ’,’NOT EQUAL!’,x .eq. y)
      write(*,’(*(g0,1x,z0,1x))’)x,x,y,y ! show decimal and hexadecimal value
      write(*, *)’regular’,x .eq. y, x .gt. y, x .lt. y ! standard operators
      ! For the default ULP=1.0, the relational operators can be used
      write(*, *)’compare’,x .equalto. y, x .greaterthan. y, x .lessthan. y
      do i=0,10
         ulp=real(i,kind=wp)/2.0
         write(*,*) i, compare_float( x, y, ulp=ulp ) ,’ULP=’,ulp
      enddo
   end program demo_compare_float

Results:

    >  is 10*0.1 == 1.0?
    >  NOT EQUAL!
    > 1.00000012 3F800001 1.00000000 3F800000
    >  regular F T F
    >  compare F T F
    >            0 F ULP=   0.00000000
    >            1 F ULP=  0.500000000
    >            2 F ULP=   1.00000000
    >            3 T ULP=   1.50000000
    >            4 T ULP=   2.00000000
    >            5 T ULP=   2.50000000
    >            6 T ULP=   3.00000000
    >            7 T ULP=   3.50000000
    >            8 T ULP=   4.00000000
    >            9 T ULP=   4.50000000
    >           10 T ULP=   5.00000000


Nemo Release 3.1 compare_float (3) February 23, 2025
Generated by manServer 1.08 from b1ae8d34-dcf5-4d6f-9c5b-db36d434676f using man macros.