How to resolve the algorithm Approximate equality step by step in the Haskell programming language

Published on 7 June 2024 03:52 AM

How to resolve the algorithm Approximate equality step by step in the Haskell programming language

Table of Contents

Problem Statement

Sometimes, when testing whether the solution to a task (for example, here on Rosetta Code) is correct, the difference in floating point calculations between different language implementations becomes significant. For example, a difference between 32 bit and 64 bit floating point calculations may appear by about the 8th significant digit in base 10 arithmetic.

Create a function which returns true if two floating point numbers are approximately equal.

The function should allow for differences in the magnitude of numbers, so that, for example, 100000000000000.01   may be approximately equal to   100000000000000.011, even though   100.01   is not approximately equal to   100.011. If the language has such a feature in its standard library, this may be used instead of a custom function. Show the function results with comparisons on the following pairs of values:

Answers should be true for the first example and false in the second, so that just rounding the numbers to a fixed number of decimals should not be enough. Otherwise answers may vary and still be correct. See the Python code for one type of solution.

Let's start with the solution:

Step by Step solution about How to resolve the algorithm Approximate equality step by step in the Haskell programming language

1. Custom Equality Operator for Near Equality

The code defines a type class AlmostEq that provides a way to compare two values of type a for near equality (approximate equality). This is useful when comparing floating-point numbers or other values where exact equality may not be possible due to rounding errors or other factors.

2. Components of AlmostEq

The AlmostEq type class requires the following constraints on its argument a:

  • Num a: a must be a numeric type that supports basic arithmetic operations.
  • Ord a: a must be an ordered type, meaning values of type a can be compared using <, <=, etc.
  • Eq a: a must be an equality type, meaning values of type a can be tested for equality using ==.

3. eps Member Function

The AlmostEq type class includes a member function eps that returns an epsilon value which represents the maximum difference allowed between two values for them to be considered nearly equal.

4. ~= Operator for Near Equality

The ~= operator is defined as an infix operator with a precedence of 4. It is used to test whether two values of type a are nearly equal. The operator returns True if any of the following conditions are met:

  • The values are exactly equal (a == b).
  • The absolute difference between the values is less than the eps value multiplied by the absolute sum of the values (abs (a - b) < eps * abs(a + b)).
  • The absolute difference between the values is less than the eps value (abs (a - b) < eps).

5. Instances of AlmostEq

The code also provides instances of the AlmostEq type class for the following types:

  • Int with eps set to 0.
  • Integer with eps set to 0.
  • Double with eps set to 1e-14.
  • Float with eps set to 1e-5.

6. test List

The test list contains several pairs of floating-point numbers to be tested for near equality.

7. main Function

The main function runs the tests by iterating through the test list and calling the runTest function for each pair of numbers.

8. runTest Function

The runTest function prints the following information for each pair of numbers:

  • The original values a and b.
  • The result of the exact equality test (a == b).
  • The result of the near equality test (a~=b).

The code is a Haskell program that defines a function main, which is the entry point of the program. The function main takes a list of tuples of pairs of floating point numbers as input and for each pair it prints if the two numbers are equal or if they are almost equal.

The definition of main is as follows:

main = mapM_ runTest test
 where
   runTest (a, b) = do
     printf "%f == %f %v\n" a b (show $ a==b) :: IO ()
     printf "%f ~= %f %v\n\n" a b (show $ a~=b)

The function mapM_ applies the function runTest to each element of the list test and returns a list of the results. The function runTest takes a pair of floating point numbers as input and prints if the two numbers are equal or if they are almost equal. The function printf is used to format the output.

The definition of the function runTest is as follows:

runTest (a, b) = do
 printf "%f == %f %v\n" a b (show $ a==b) :: IO ()
 printf "%f ~= %f %v\n\n" a b (show $ a~=b)

The function printf is used to format the output. The format string "%f == %f %v\n" expects three arguments: two floating point numbers and a boolean value. The format string "%f ~= %f %v\n" expects three arguments: two floating point numbers and a boolean value.

The function show is used to convert the boolean value to a string. The boolean value is True if the two numbers are equal or if they are almost equal, and False otherwise.

The definition of the function ~= is as follows:

infix 4 ~=
(~=) :: AlmostEq a => a -> a -> Bool
a ~= b = or [ a == b
           , abs (a - b) < eps * abs(a + b)
           , abs (a - b) < eps ]

The infix operator ~= is defined for any type a that implements the AlmostEq type class. The type class AlmostEq requires the type a to implement the Num, Ord, and Eq type classes. The function eps returns the epsilon value for the type a. The epsilon value is used to determine if two numbers are almost equal.

The function ~= returns True if the two numbers are equal, if the absolute value of the difference between the two numbers is less than the epsilon value multiplied by the absolute value of the sum of the two numbers, or if the absolute value of the difference between the two numbers is less than the epsilon value. Otherwise, the function returns False.

The definition of the AlmostEq type class is as follows:

class (Num a, Ord a, Eq a) => AlmostEq a where
 eps :: a

The AlmostEq type class has one method, eps, which returns the epsilon value for the type a. The epsilon value is used to determine if two numbers are almost equal.

The following instances of the AlmostEq type class are defined:

instance AlmostEq Int where eps = 0
instance AlmostEq Integer where eps = 0
instance AlmostEq Double where eps = 1e-14
instance AlmostEq Float where eps = 1e-5

The instance of the AlmostEq type class for the type Int sets the epsilon value to 0. The instance of the AlmostEq type class for the type Integer sets the epsilon value to 0. The instance of the AlmostEq type class for the type Double sets the epsilon value to 1e-14. The instance of the AlmostEq type class for the type Float sets the epsilon value to 1e-5.

The following is an example of how to use the ~= operator:

>>> 10 ~= 10.0
True
>>> 10 ~= 10.001
True
>>> 10 ~= 11
False

Source code in the haskell programming language

class (Num a, Ord a, Eq a) => AlmostEq a where
  eps :: a

infix 4 ~=
(~=) :: AlmostEq a => a -> a -> Bool
a ~= b = or [ a == b
            , abs (a - b) < eps * abs(a + b)
            , abs (a - b) < eps ]

instance AlmostEq Int where eps = 0
instance AlmostEq Integer where eps = 0
instance AlmostEq Double where eps = 1e-14
instance AlmostEq Float where eps = 1e-5


test :: [(Double, Double)]
test = [(100000000000000.01, 100000000000000.011)
       ,(100.01, 100.011)
       ,(10000000000000.001 / 10000.0, 1000000000.0000001000)
       ,(0.001, 0.0010000001)
       ,(0.000000000000000000000101, 0.0)
       ,(sqrt 2 * sqrt 2, 2.0)
       ,(-sqrt 2 * sqrt 2, -2.0)
       ,(3.141592653589793, 3.141592653589794)
       ,(3.141592653589, 3.141592653589794)]

-- requires import Text.Printf
main = mapM_ runTest test
  where
    runTest (a, b) = do
      printf "%f == %f %v\n" a b (show $ a==b) :: IO ()
      printf "%f ~= %f %v\n\n" a b (show $ a~=b)


  

You may also check:How to resolve the algorithm One-dimensional cellular automata step by step in the ALGOL W programming language
You may also check:How to resolve the algorithm Elementary cellular automaton/Infinite length step by step in the Wren programming language
You may also check:How to resolve the algorithm Sequence of primorial primes step by step in the Perl programming language
You may also check:How to resolve the algorithm Loops/For step by step in the AppleScript programming language
You may also check:How to resolve the algorithm Strip whitespace from a string/Top and tail step by step in the Elixir programming language