I am looking for advice on how to design the following scenario:
I have a 3-dimensional vector class, the entries are of type double
. I want two vectors to be considered equal if their corresponding elements differ by less than a given tolerance, e.g. 10^(-10).
(Code examples are in C# syntax mixed with pseudocode.)
public class Vector
{
double x, y, z;
public bool TolEquals(Vector other)
{
return ((|x-other.x| < 10^(-10)) // absolute value of difference
&& (|y-other.y| < 10^(-10)) // must be small enough
&& (|z-other.z| < 10^(-10)));
}
}
I need a certain flexibility because other people might want to use another tolerance value than 10^(-10).
That means, I need to somehow incorporate the tolerance value into my equality checking method, e.g. so:
public bool TolEquals(Vector other, double tolerance)
{
return ((|x-other.x| < tolerance)
&& (|y-other.y| < tolerance)
&& (|z-other.z| < tolerance);
}
But I know that throughout my application, the tolerance will be constant. And I do not want to write
v1.TolEquals(v2, tolerance)
every time I check for equality since it is not very readable and since typos can happen easily.
Another idea is to store tolerance
as a member of the Vector
class and set it in the constructor of Vector
. Then the TolEquals
method needs only one argument:
public class Vector
{
double x, y, z;
double tolerance;
public Vector(double x, double y, double z, double tolerance)
{
this.x = x;
this.y = y;
this.z = z;
this.tolerance = tolerance;
}
public bool TolEquals(Vector other)
{
return ((|x-other.x| < tolerance)
&& (|y-other.y| < tolerance)
&& (|z-other.z| < tolerance));
}
}
But then someone using my code could accidentally create two vectors v
and w
with different tolerances and would get different answers from v.TolEquals(w)
and w.TolEquals(v)
. That's not supposed to happen either.
I will probably add some static methods to my Vector
class sooner or later that take several vectors as input and do some calculation involving TolEquals
with them. So I need to make sure that all vectors use the same tolerance value.
I am looking for a solution that allows me to define tolerance
somewhere in the program, outside the Vector
class, and then all vectors use that tolerance
for their TolEquals
. And I want to be able to change tolerance
at run-time (don't need it now but might need it later), resulting in all vectors using the new value instead.
BUT it might be that someone using my code needs two different tolerance values coexisting. He would have two distinct groups of vectors, using the different tolerance values, but the groups do not interact with each other.
So a static field tolerance
is no option either.
I will also write a matrix
class and maybe some others that will have an analogue of TolEquals
, so the solution should not be limited to my Vector
class.
Any ideas?
EDIT:
Inspired by the accepted answer I feel like presenting my final solution (in C#) and put in some comments. All information is given in the accepted answer, but it took me some time to process it so I'll refine it a bit here. I decided to implement it with a static field for the tolerance in the generic vector class:
public interface ISpace { double TOL(); }
public class ZeroTolSpace : ISpace { public double TOL() => 0; }
public class Tol10Space : ISpace { public double TOL() => 1E-10; }
// Others can easily write new classes for other tolerance values.
public class Vector<T> where T: ISpace, new()
{
private double x, y, z;
public static double? tol = null; // Changeable at runtime (if really needed)!
public Vector(double x, double y, double z)
{
this.x = x; this.y = y; this.z = z;
tol = tol ?? (new T()).TOL(); // Since tol is static, the new-operator
// gets called only once, minimzing overhead.
}
public bool TolEquals(Vector other)
{
return ((Math.Abs(x-other.x) < tol)
&& (Math.Abs(y-other.y) < tol)
&& (Math.Abs(z-other.z) < tol));
}
}
Then in the Main
method we can do:
Vector<ZeroTolSpace> v1 = new Vector<ZeroTolSpace> (0, 0, 0);
Vector<ZeroTolSpace> v2 = new Vector<ZeroTolSpace>(1E-11, 1E-11, 1E-11);
v1.TolEquals(v2)); // false since _tol is 0
Vector<Tol10Space> w1 = new Vector<Tol10Space> (0, 0, 0);
Vector<Tol10Space> w2 = new Vector<Tol10Space> (1E-11, 1E-11, 1E-11);
w1.TolEquals(w2)); // true since tol is 1E-10
v1.TolEquals(w2)); // compile error: types do not match
Matrix and other classes depending on tolerance can be implemented in the same way.
ToEquals
should check that both vectors have the same tolerance.10^(-10)
uses the bitwise XOR operator (^
) and is equal to-4
. what you want is1e-10
.