How to fix Precision problems

214 views Asked by At

I have several classes that follow the same basic concept for dealing with units.

Here is a simple example of one being used in a unit test:

    [Test()]
    public void Angle_MathOperatorTest()
    {
        Angle a1 = new Angle(AngleType.Degree, 360);
        Angle a2 = new Angle(AngleType.Radian, Math.PI * 2);

        Angle addedAngle = a1 + a2;
        addedAngle.Degrees.ShouldBeEquivalentTo(720);

        Angle subtractedAngle = a1 - a2;
        subtractedAngle.Radians.ShouldBeEquivalentTo(0);
    }

I have made several classes like this demonstrated Angle Class, covering other basic unit types.

The specific class that revealed to me that I had a precision problem was the use of the class that handles units of length: Dimension

I have helped build a basic Geometry Library utilizing this Dimension class as its basic unit type. For Example, here is the Point class:

public class Point
{

    public Dimension X;

    public Dimension Y;

    public Dimension Z;
}

Lines and other shapes have properties like Length that are represented by Dimensions and endpoints that are built using this Point Class.

The problem becomes evident when I attempt to tell if those lines are all parallel. As in this function:

    /// <summary>
    /// checks to see whether every line is parallel
    /// </summary>
    /// <param name="passedLines">passed List of Lines</param>
    /// <returns></returns>
    public static bool AreAllParallel(this List<Line> passedLines)
    {

        for (int i = 0; i < passedLines.Count - 1; i++)
        {
            if (!passedLines[i].IsParallelTo(passedLines[i + 1]))
            {
                return false;
            }
        }

        return true;
    }

It will often return false because it is checking for too high of a precision. After doing Rotations and Translations with Points and lines, the rounding adds up to just enough to make this function return false when I would like for it to return true.

So:

Which set of the following choices is the correct/better choice?

  • just doing a check in functions like IsParallelTo for the numbers being relatively close (e.g. Within .0001 inches)

If(Math.Abs( thing.x - thing2.x) < .0001)

  • improving upon the previous idea with a variable constant that is pulled from a configuration file, therefore allowing the user to choose the desired acceptable deviation

If(Math.Abs( thing.x - thing2.x) < Properties.AcceptedDeviationConstant)

  • or reducing the problem at the root level in the Dimension Class:

I can either use the same strategy

//inside Dimension Equals
public override bool Equals(object obj)
{
    return (Math.Abs(this.Inches - ((Dimension)(obj)).Inches)) < Constants.AcceptedEqualityDeviationConstant;
}

It would really look like this but the above is easier to understand

return (Math.Abs(this.GetValue(this.InternalUnitType) - ((Dimension)(obj)).GetValue(this.InternalUnitType))) < Constants.AcceptedEqualityDeviationConstant;

Or finally, my last idea is to replace everything in my unit classes to the base unit of Decimal instead of Double (Dimension, Angle, Etc.) to Decimal and somehow (After studying up on it) figure out if that will help.


How and where should I improve the consistency of precision in my classes' equality operations?

p.s. The libraries are open-source and can be found (Units here and Geometry here)

1

There are 1 answers

0
John Alexiou On

If e1 is the direction of line one and e2 the direction of line two then to check if they are parallel within a slope tolerance of θ (radians) do the following (pseudo code):

bool is_parallel = |Cross(e1,e2)| <= |e1|*|e2|*Cos(θ)

where |v| is the vector magnitude and Cross() is the vector cross product.