3 min read

Real Epsilon Equality

Take an IEC 61131-3 Structured Text function. It compares two REAL numbers for equality within a specified epsilon tolerance.

FUNCTION REPSILONEQ : BOOL
VAR_INPUT
    rLHS     : REAL; (* nominal left-hand side *)
    rRHS     : REAL; (* nominal right-hand side *)
    rEpsilon : REAL := REAL#1e-6; (* allowable difference *)
END_VAR

(* Error band is inclusive of the epsilon value.
Less than or equal to is close enough, rather than strict equality. *)
REPSILONEQ := ABS(rLHS - rRHS) <= rEpsilon;
END_FUNCTION

It takes two arguments: two real numbers, nominally the left- and right-hand sides of a function. In reality, the sides do not matter for this function; it computes their absolute difference, hence order does not matter. The sign disappears.

But it begs a question:

Equal To, or Less Than or Equal To?

Should the comparison operator be < or <=? Less than, or less than or equal to? Put another way, is the epsilon an inclusive or exclusive upper boundary for the difference comparison? What should happen at the epsilon boundary?

Ad extremis, as the Romans might say, epsilon is the smallest meaningful positive difference representable in the system’s floating-point implementation of a REAL number; C implements two macros, FLT_EPSILON and DBL_EPSILON, defining these cryptic numbers. CPUs implement “real” numbers as floating-point numbers which comprise two signed integer components: an exponent and a mantissa. The smallest such number amounts to the largest negative exponent combined with the smallest mantissa—smallest just above zero.

Smallest Epsilon

As shown above, the implementation compares the difference between its two inputs against an inclusive upper boundary.

Suppose that \(\epsilon\) (the rEpsilon argument) is the smallest possible difference—the actual floating-point unit’s version of epsilon in real space, whatever that might be. Not zero, that is, but one floating-point step above zero.

In that case, the REPSILONEQ function answers TRUE only if the absolute difference computes as an absolute \(lhs-rhs=[0,\epsilon]\) result. Two possible floating-point numbers satisfy the equality test: either zero or epsilon.

Only an absolute \(\epsilon\lt0\) would fail for all values of \(lhs\) and \(rhs\). Negative epsilons always answer FALSE simply because the absolute difference (always a positive number) can never be less than or equal to a negative number.

Conclusions

The REPSILONEQ function provides a way to compare two real numbers with tolerance for small differences.

Using an inclusive epsilon ensures that values within the error tolerance have equality. Call it a pragmatic choice.

Recommended behaviour:

  • Reject negative epsilon values. Avoid negative epsilon values, as they will always result in FALSE. Clamp its lower boundary to zero using LIMIT.
  • Choose an appropriate rEpsilon value based on the usage precision requirements. Use an appropriate inclusive error band to account for small floating-point differences.

Be aware that the function only considers the absolute difference, not relative differences. It does not account for the scale of the numbers being compared.