Approximately Equals Operator
=============================
This is a proposal for the inclusion of two new operators into Cforall.
Approximate equality is a very useful concept for floating point arithmetic.
Due to floating point error values are very rarely the same when they are
supposed to be. Because of this it is standard practice to provide some
wrapper function (or in C++ an entire class) when doing these operations.
This proposal offers to do the same thing, but introduces a new trinary
operator to provide an easy to read interface for this operation.
For example if you have two floats `a` and `b` to see if they are equal with
error `e` you would write `a ~== b : e`. The underlying function might look
like the following:
```
bool ?~==?:?(float lhs, float rhs, float epsilon) {
return lhs <= rhs + epsilon && rhs <= lhs + epsilon
}
```
Approximately Inequals Operator
------------------------------
Called `?~!=?:?` and usually written as `a ~!= b : e`, this is the negated
variant of approximately equals. That's it one should always be equal to the
negation of the other.
Although this is not commonly considered a basic operation it is included to
ease negating a condition.
Default Implementation
----------------------
The provided operations do not have to example provided above. The behaviour
should be to return true if the absolute value of the difference between the
compared values is less than or equal to the error value. So approximate
equality with error of zero should be the same as equality.
Each implementation could be provided individually or it could be provided
through a generic function, as in the following:
```
forall(otype T | { T ?+?(T, T); bool ?<=?(T, T); })
bool ?~==?:?(float lhs, float rhs, float epsilon) {
return lhs <= rhs + epsilon && rhs <= lhs + epsilon
}
```
This could be organized like the concurrency extensions. That is the minimal
syntax is provided but to get full use a library include is required. If so
the library might be called `approx.hfa`.
Required Syntax
---------------
We will need `~==` and `~!=` as new operator tokens. `?~==?:?` and `?~!=?:?`
must be recognized as two new special operator names. Then the operators
have to be included in the grammar as a new type of expression.
Because of the natural use it should bind more tightly than logical operations
so that `b && x ~== y : e` is the same as `b && (x ~== y : e)` but not as much
arithmetic operations so `n + x ~== y : e` is the same as `(n + x) ~== y : e`.
Either at the same precedence as equality or in-between equality and
comparison.
Choice of Symbols
-----------------
The first operator symbols (`~==` and `~!=`) were chosen considering that the
only two operations we are adding approximate variants for. (See end of
section for details.) If only approximately equals was included then `~=`
might be the better choice for consistency with `!=`, `<=` and `>=`. However
that would make it less consistent with `~!=` and we can't use both `~` and
`!` to replace the first symbol in that case.
The `:` for the second separator was used because of symmetry with the
conditional operator `?:`. The symmetry is not perfect, the colon on the
conditional is a divider that separates two equivalent options while here it
adds an extra detail to the main operation, but the additional separator for
a third argument remains the same. In addition `:` is already a token and it
never appears to begin something so it is unlikely to ever cause conflicts.
### Why Not Add More Approximate Operators?
To begin with very few operations have a meaningful error value in them.
Besides some exotic recursive calculation it is limited to comparisons.
With equality and inequality covered that leaves use with the ordering
comparisons. In these cases because one side (above or below) is covered in
its entirety adding an error simply shifts (down or up) by that amount. This
is a less useful operation and easier to code in line. For example:
`( a ~<= b : epsilon ) == ( a <= b + epsilon )`
Default Epsilon
---------------
For primitive (or library) types that have approximate equality defined on
them it may also be useful to provide a general default for the error. Some
epsilon that is small, but large enough to catch the usual build up of error
when someone is doing just a bit of math with these types.
However it should probably not be called epsilon to avoid any confusion with
machine epsilon, which in many cases would be much smaller than the default
error value.
Even if the default implementations are in the prelude and not a library,
these might be useful contexts of `approx.hfa`.