Approximate equality

From Rosetta Code
Approximate equality is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.

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.


Task

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:

  •     100000000000000.01,   100000000000000.011     (note: should return true)
  •     100.01,   100.011                                                     (note: should return false)
  •     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.14159265358979323846,   3.14159265358979324


Answers should be similar to the Python code example, except for the fifth case, which may well vary depending on how your function handles comparison with (exactly) 0.0.

Factor

The ~ word takes three arguments: the two values to be compared, and an epsilon value representing the allowed distance between the two values. A positive epsilon performs an absolute distance test, an epsilon of zero performs an exact comparison, and a negative epsilon performs a relative distance test (as required by this task).

Works with: Factor version 0.99 development version 2019-07-10

<lang factor>USING: formatting generalizations kernel math math.functions ;

100000000000000.01 100000000000000.011 100.01 100.011 10000000000000.001 10000.0 /f 1000000000.0000001000 0.001 0.0010000001 0.000000000000000000000101 0.0 2 sqrt dup * 2.0 2 sqrt dup neg * -2.0 3.14159265358979323846 3.14159265358979324

[ 2dup -1e-15 ~ "%+47.30f %+47.30f -1e-15 ~ : %u\n" printf ] 2 8 mnapply</lang>

Output:
+100000000000000.015625000000000000000000000000 +100000000000000.015625000000000000000000000000 -1e-15 ~ : t
            +100.010000000000005115907697472721             +100.010999999999995679900166578591 -1e-15 ~ : f
     +1000000000.000000238418579101562500000000      +1000000000.000000119209289550781250000000 -1e-15 ~ : t
              +0.001000000000000000020816681712               +0.001000000100000000054917270731 -1e-15 ~ : f
              +0.000000000000000000000101000000               +0.000000000000000000000000000000 -1e-15 ~ : f
              +2.000000000000000444089209850063               +2.000000000000000000000000000000 -1e-15 ~ : t
              -2.000000000000000444089209850063               -2.000000000000000000000000000000 -1e-15 ~ : t
              +3.141592653589793115997963468544               +3.141592653589793115997963468544 -1e-15 ~ : t

Perl 6

Works with: Rakudo version 2019.07.1

Is approximately equal to is a built-in operator in Perl 6. Unicode ≅, or the ASCII equivalent: =~=. By default it uses a tolerance of 1e-15 times the order of magnitude of the larger comparand, though that is adjustable by setting the dynamic variable $*TOLERANCE to the desired value. Probably a good idea to localize the changed $*TOLERANCE as it will affect all comparisons within its scope.

<lang perl6>for

   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,
   100000000000000003.0, 100000000000000004.0,
   3.14159265358979323846, 3.14159265358979324
 -> $a, $b {
   say "$a ≅ $b: ", $a ≅ $b;

}

say "\nTolerance may be adjusted.";

say 22/7, " ≅ ", π, ": ", 22/7 ≅ π; { # Localize the tolerance to only this block

 my $*TOLERANCE = .001;
 say 22/7, " ≅ ", π, ": ", 22/7 ≅ π;

}</lang>

Output:
100000000000000.01 ≅ 100000000000000.011: True
100.01 ≅ 100.011: False
1000000000.0000001 ≅ 1000000000.0000001: True
0.001 ≅ 0.0010000001: False
0.000000000000000000000101 ≅ 0: True
2.0000000000000004 ≅ 2: True
-2.0000000000000004 ≅ -2: True
100000000000000003 ≅ 100000000000000004: True
3.141592653589793226752 ≅ 3.14159265358979324: True

Tolerance may be adjusted.
3.142857 ≅ 3.141592653589793: False
3.142857 ≅ 3.141592653589793: True

Phix

Traditionally I have always just used sprintf() to compare floating point atoms in phix.
For this task, it proved much harder to get decent-looking output, than it did to perform the tests, and to that end I decided to allow the display format (dfmt) to be overidden, when needed, and for the tricker/ambiguous test 5, I also allow the compare format (cfmt) to be overidden, getting both a true and false result. Likewise I have a different result for test 4 to everyone else, but simply setting the cfmt to "%.8f" would get it the NOT. <lang Phix>procedure test(atom a,b, string dfmt="%g", cfmt="%g")

   bool eq = sprintf(cfmt,a)==sprintf(cfmt,b)
   string eqs = iff(eq?"":"NOT "),
          sa = sprintf(dfmt,a),
          sb = sprintf(dfmt,b)
   printf(1,"%30s is %sapproximately equal to %s\n",{sa,eqs,sb})

end procedure

test(100000000000000.01,100000000000000.011,"%.3f") test(100.01,100.011) test(10000000000000.001/10000.0,1000000000.0000001000,"%.10f") test(0.001,0.0010000001,"%.8f") test(0.000000000000000000000101,0.0,"%f") test(0.000000000000000000000101,0.0,"%f","%6f") test(sqrt(2)*sqrt(2),2.0) test(-sqrt(2)*sqrt(2),-2.0) test(3.14159265358979323846,3.14159265358979324,"%.20f")</lang>

Output:

64 bit (implied by some of the accuracies specified for this task):

           100000000000000.010 is approximately equal to 100000000000000.011
                        100.01 is NOT approximately equal to 100.011
         1000000000.0000001001 is approximately equal to 1000000000.0000001000
                 0.00100000000 is approximately equal to 0.0010000001
 0.000000000000000000000101000 is NOT approximately equal to 0.000000
 0.000000000000000000000101000 is approximately equal to 0.000000
                             2 is approximately equal to 2
                            -2 is approximately equal to -2
        3.14159265358979323851 is approximately equal to 3.14159265358979324003

32 bit (in fact a couple of them, the first and last pairs, are actually genuinely identical):

           100000000000000.016 is approximately equal to 100000000000000.016
                        100.01 is NOT approximately equal to 100.011
         1000000000.0000002384 is approximately equal to 1000000000.0000001192
                  0.0010000000 is approximately equal to 0.0010000001
 0.000000000000000000000101000 is NOT approximately equal to 0.000000
 0.000000000000000000000101000 is approximately equal to 0.000000
                             2 is approximately equal to 2
                            -2 is approximately equal to -2
            3.1415926535897931 is approximately equal to 3.1415926535897931

Python

<lang python>from numpy import sqrt from math import isclose

testvalues = [[100000000000000.01,100000000000000.011], [100.01, 100.011],

             [10000000000000.001 / 10000.0, 1000000000.0000001000],
             [0.001, 0.0010000001], [0.00000000000000000101, 0.0],
             [sqrt(2) * sqrt(2), 2.0], [-sqrt(2) * sqrt(2), -2.0],
             [100000000000000003.0, 100000000000000004.0],
             [3.14159265358979323846, 3.14159265358979324]]

for (x, y) in testvalues:

   maybenot = "" if isclose(x, y) else "NOT"
   print(x, "is", maybenot, "approximately equal to ", y)

</lang>

Output:
100000000000000.02 is  approximately equal to  100000000000000.02
100.01 is NOT approximately equal to  100.011
1000000000.0000002 is  approximately equal to  1000000000.0000001
0.001 is NOT approximately equal to  0.0010000001
1.01e-18 is NOT approximately equal to  0.0
2.0 is  approximately equal to  2.0
-2.0 is  approximately equal to  -2.0
1e+17 is  approximately equal to  1e+17
3.141592653589793 is  approximately equal to  3.141592653589793

REXX

Since the REXX language uses decimal digits for floating point numbers (and integers),   it's just a matter of choosing
the number of decimal digits for the precision to be used for arithmetic   (in this case, fifteen decimal digits). <lang rexx>/*REXX program mimics an "approximately equal to" for comparing floating point numbers*/ numeric digits 15 /*what other FP hardware normally uses.*/ @.= /*assign default for the @ array. */ parse arg @.1 /*obtain optional argument from the CL.*/ if @.1= | @.1=="," then do; @.1= 100000000000000.01 100000000000000.011

                                  @.2= 100.01                       100.011
                                  @.3= 10000000000000.001 / 10000   1000000000.0000001000
                                  @.4= 0.001                        0.0010000001
                                  @.5= 0.00000000000000000000101    0.0
                                  @.6=  sqrt(2) * sqrt(2)           2.0
                                  @.7= -sqrt(2) * sqrt(2)           '-2.0'
                                  @.8= 100000000000000003.0         100000000000000004.0
                                  @.9= 3.14159265358979323846       3.14159265358979324
                            end
    do j=1  while @.j\==                      /*process CL argument or the array #s. */
    say
    say center(' processing pair ' j" ",71,'═') /*display a title for the pair of #s.  */
    parse value  @.j  with  a  b                /*extract two values from a pair of #s.*/
    say 'A='   a                                /*display the value of  A  to the term.*/
    say 'B='   b                                /*   "     "    "    "  B   "  "    "  */
    say right('A approximately equal to B?', 65)   word("false true", 1 + approxEQ(a,b) )
    end   /*j*/                                 /* [↑]  right─justify text & true/false*/

exit /*stick a fork in it, we're all done. */ /*──────────────────────────────────────────────────────────────────────────────────────*/ approxEQ: procedure; parse arg x,y; return x=y /*floating point compare with 15 digits*/ /*──────────────────────────────────────────────────────────────────────────────────────*/ sqrt: procedure; parse arg x; if x=0 then return 0; d=digits(); numeric digits; h=d+6

     numeric form; m.=9; parse value format(x,2,1,,0) 'E0' with g "E" _ .; g=g *.5'e'_ %2
       do j=0  while h>9;      m.j=h;               h=h%2+1;       end  /*j*/
       do k=j+5  to 0  by -1;  numeric digits m.k;  g=(g+x/g)*.5;  end  /*k*/; return g/1</lang>
output   when using the internal default inputs:
═════════════════════════ processing pair  1 ══════════════════════════
A= 100000000000000.01
B= 100000000000000.011
                                      A approximately equal to B? true

═════════════════════════ processing pair  2 ══════════════════════════
A= 100.01
B= 100.011
                                      A approximately equal to B? false

═════════════════════════ processing pair  3 ══════════════════════════
A= 1000000000
B= 1000000000.0000001000
                                      A approximately equal to B? true

═════════════════════════ processing pair  4 ══════════════════════════
A= 0.001
B= 0.0010000001
                                      A approximately equal to B? false

═════════════════════════ processing pair  5 ══════════════════════════
A= 0.00000000000000000000101
B= 0.0
                                      A approximately equal to B? false

═════════════════════════ processing pair  6 ══════════════════════════
A= 2.00000000000000
B= 2.0
                                      A approximately equal to B? true

═════════════════════════ processing pair  7 ══════════════════════════
A= -2.00000000000000
B= -2.0
                                      A approximately equal to B? true

═════════════════════════ processing pair  8 ══════════════════════════
A= 100000000000000003.0
B= 100000000000000004.0
                                      A approximately equal to B? true

═════════════════════════ processing pair  9 ══════════════════════════
A= 3.14159265358979323846
B= 3.14159265358979324
                                      A approximately equal to B? true