huB1erTi2
huB1erTi2

Reputation: 23

Conversion from degrees to radians not accurate enough

I know very well how conversion from degrees to radians works, but I still have a problem with it. The problem is its accuracy.

As you probably know trigonometric functions from System.Math library use radians, but I needed trigonometric functions, in which you plug degrees. So I created a Helper class with Cos,Sin and Tan functions, which would just convert degrees to radians inside of them and call their Math functions.

public static class Helper
{
    public static double Sin(double degrees) => Math.Sin(degrees * Math.PI / 180.0);
    public static double Cos(double degrees) => Math.Cos(degrees * Math.PI / 180.0);
    public static double Tan(double degrees) => Math.Tan(degrees * Math.PI / 180.0);
}

But, for example, when I use Math.Tan(45) it returns 0.999999999989 instead of one. My question to you, my fellow friends, is how to make this totally accurate.

Thanks in advance.

EDIT: I understand, there will be small errors and it's impossible to get rid of them. What I exactly want to know is how to round the value by the needed amount (not too much, not too little) so it works anyway.

EDIT2: Context is very important in this question, so here it is:

I need trigonometry for my Line class which has following constructor:

public Line(Vector2 beginning, Vector2 end)
{
    this.beginning = beginning;
    this.end = end;
    this.direction = 180.0 * Math.Atan(end.Y - beginning.Y / end.X - beginning.X) / Math.PI;
    this.slope = Helper.Tan(direction);
    this.length = Helper.DistanceBetweenTwoPoints(beginning, end);
    this.offset = beginning.X;
 }

The problem arises with the line where I assign the value for the slope, which I need for checking if a point is on this line using linear algebra.

DISCLOSURE: Vector2 is a structure with double x and double y. That's all.

Upvotes: 0

Views: 1910

Answers (2)

Blindman67
Blindman67

Reputation: 54099

Using the slope that you get via Helper.Tan(direction) to calculate if a point is on a line is going to produce a differing amounts of error for different slopes that get get worse as direction approaches +/- PI / 2 because tan(x) approaches infinity as x approaches +/- PI/2 and at +/- PI/2 it will not work at all.

To see if a point is near a line use the cross product of the line's vector and the vector to the point. Eg the line is from point A to B and the point is P then Cross(B-A,P-A) will be zero when P is on the line. To combat the error which is related to the line length divide the cross product by the line length squared and compare to a arbitrary small tolerance epsilon.

// A,B, P are Vector2
double cross = (B.x-A.x) * (P.y-A.y) - (B.y-A.y) * (P.x-A.x);
cross /= (B.x-A.x) * (B.x-A.x) + (B.y-A.y) * (B.y-A.y);
double epsilon = 1.0e-6; // 1 millionths of a unit
if (Math.abs(cross) < epsilon) {
     // point P is on line A,B
} 

BTW Math.PI is an approximation of an irrational number, using it will always introduce an error no matter what system you use. PI simply can not be written as a number. In the real world all measurements have an error and for all practical applications of PI 7 digits 3.141592 are more than enough precision.

Upvotes: 1

Peter Schneider
Peter Schneider

Reputation: 1701

You will always have rounding errors when using doubles. That's the very nature of floating point arithmetics. Double can carry approximatly 16 decimal digits. And on every operation the error adds up. The "trick" is to accept this error, never compare doubles/ floats with equality (unless you know what you are doing) and do meaningful rounding on output.

See http://floating-point-gui.de/.

To decide if a point lies on a line between two points or similar problems, you don't need to round but use a tolerance: calculate the shortest distance between the point and the line and check if it is below a threshold. How large this threshold can/ should be depends on your application and there is no easy answer. If for example your points are in the range of 0..10, a tolerance of 1e-7 would be reasonable for me. If you want a generic implementation you should take the maximum of

  • the distance of the points times a factor (e.g. dist(begin, end) * 1e-8)
  • an absolute value for very small distances (e.g. 1e-10)

But you can still run into problems. For example, if you line goes from (10000000.1,0) to (10000000.2,0), see here for details. This is a broad topic in general.

Upvotes: 3

Related Questions