Jacopo Grassi - 5 months ago 21
Java Question

# Calculate angle between 3 points with vector, more precision needed (java)

I need to calculate the angle between 3 points. I've done that using vectors, it looks like it's working, but sometimes I get NaN as a result. To calculate the angle I used the arcos(dot(v1,v2)/(length(v1)*length(v2))) formula. Here's the code:

``````private static double angleBetween(Point previous, Point center, Point next) {
Vector2D vCenter = new Vector2D(center.x, center.y );
Vector2D vPrev = new Vector2D(previous.x, previous.y );
Vector2D vNext = new Vector2D(next.x, next.y );

Vector2D a = vCenter.minus(vPrev);
Vector2D b = vCenter.minus(vNext);

double dot = Vector2D.dot(a, b);

double la = a.length();

double lb = b.length();

double l = la*lb;

double acos = Math.acos(dot/l);

double deg = Math.toDegrees(acos);

return deg;

}
``````

Vector2D class:

``````public double length() {
return Math.sqrt(x * x + y * y);
};

public static double dot(Vector2D v1, Vector2D v2) {
return v1.x * v2.x + v1.y * v2.y;
};

public Vector2D minus(Vector2D v) {
return new Vector2D(x - v.x, y - v.y);
};
``````

Debugging the program I've discovered why this happens. for example let be:

``````center = (127,356)
previous = (117,358)
next = (137,354)

//calculating the vectors
a = (-10,2) //center - prev
b = (10,-2) //center - next

dot = -10*10 + 2*-2 = 104

la = sqrt(-10*-10 + 2*2) = sqrt(104) = see attachment
lb = sqrt(10*10 + -2*-2) = sqrt(104) = see attachment

l = la * lb = see attachment
``````

`acos = NaN`
because
`dot/l>1`
because I lost precision because of
`sqrt()`
which didn't give me the exact value therefore
`la*lb`
isn't 104.

now as far as I know double is the most precise number type in java. How can I solve this problem?

PS It may looks like a very rare situation, but I'm experiencing quite a lot of them, so I can't just ignore it.

The best way to solve this problem is to use an appropriate data type like `java.math.BigDecimal` and define a precision for you computation using an instance of type `java.math.MathContext`. For instance:

``````double l = la*lb;
BigDecimal lWrapper = new BigDecimal(l, new MathContext(5));
l = lWrapper.doubleValue();
``````

There is an other way to work around this problem. Use the following formula:

``````angle = atan(length(crossProduct(a, b)) / dotProduct(a, b)) // Because the domain of definition of the tan function is R
``````

Derivation of the formula:

``````  cos(angle) = dotProduct(a, b)   / (length(a) * length(b)) and
sin(angle) = length(crossProduct(a, b)) / (length(a) * length(b))
``````

One has

`````` tan(angle) = sin(angle) / cos(angle)
``````

so

``````tan(angle) = length(crossProduct(a, b)) / (length(a) * length(b)) / dotProduct(a, b)   / (length(a) * length(b))
tan(angle) = length(crossProduct(a, b)) / dotProduct(a, b)
``````

Applying the invert function of tan:

``````angle = atan(length(crossProduct(a, b)) / dotProduct(a, b))
``````

The cross product of A, B ∈ ℜ2:

`|| A x B || = det(A,B) = ((A.x * B.y) - (A.y * B.x))`

Remarks:

1. ||x|| is the length of the vector x ⇔ length(a)
2. ∀ A, B ∈ ℜ2: || A x B || equal the determinant of A, B
3. You can use the sign(|| A x B ||) to find out the orientation