Imaginary output from arccosine function when the input is close to -1
91 views (last 30 days)
Show older comments
I am running a shell buckling simulation, where, I need to find the angle between two vectors. The first vector, m, is [0.000128276283345364, -0.000167881251734009, 7.36224543533651e-06], whereas, the second vector, n, is [-0.000128255846266013, 0.000167854504759308, -7.36107040874295e-06]. To find the angle between these vectors, I am using the following command:
m = [0.000128276283345364, -0.000167881251734009, 7.36224543533651e-06]
n = [-0.000128255846266013, 0.000167854504759308, -7.36107040874295e-06]
m_Mag = sqrt(m(1)^2 + m(2)^2 + m(3)^2);
n_Mag = sqrt(n(1)^2 + n(2)^2 + n(3)^2);
Numerator = ( m(1) * n(1) + m(2) * n(2) + m(3) * n(3) );
Denominator = m_Mag * n_Mag;
Ratio = Numerator / Denominator
theta = acos(Ratio)
The angle should be 180 degrees or pi radians, but Matlab version 2022b is giving a complex number as an output. Is there a workaround this error?

Accepted Answer
Chuguang Pan
on 15 Nov 2025 at 7:07
Edited: Torsten
on 15 Nov 2025 at 12:26
@Ali. After loading the m and n vectors from your attached NormalVectors.mat file. I find that the calculation result of theta is correct in R2024b. The codes are listed as follows, you can try this in your R2022b.
load NormalVectors.mat m n
Numerator = dot(m,n); % inner product of m and n
Denominator = vecnorm(m)*vecnorm(n);
theta = acos(Numerator/Denominator)
6 Comments
Paul
10 minutes ago
The proposed solution is not guaranteed to work.
rng(101);
v1 = randn(1,3);
v1 = v1/norm(v1); % normalized before taking the dot product.
v2 = -v1;
By construction, the dot product of v2 and v1 should be exactly -1.
format long e
d = dot(v2,v1)
Even though the dot product looks like -1, the display showing zeros past the decimal point indicates the numerical result is not what it seems. In fact
d < -1
and
acos(d)
We can see that d differs from -1 in the least significant bit
format hex
[d,-1]
Chuguang Pan
2 minutes ago
There is an exsiting answer about how to calculate the angle between two vectors. This linked answer maybe a better solution.
More Answers (1)
David Goodmanson
about 11 hours ago
Hi Ali,
The acos and (m dot n) approach is not a good way to go about this at all. It's inaccurate when n is almost equal to -m as you have. It seems like a good idea to normalize m and n, so assume that has been done. Then
m.n = cos(alpha) alpha = acos(m.n)
and alpha is very close to -pi. Let
alpha = -pi + theta
where theta is small, and is the amount by which alpha falls short of -pi.
Define q = -n so that m and q are amost identical. Then a much better method, which treats small differences in linear fashion, shows that
d = m-q
theta = |d|
In your case
theta = 9.7767e-09. % which is tiny
alpha = -pi + 9.7767e-09
Details:
First of all, consider
m.n = cos(alpha) alpha = acos(m.n),
Since m.n is almost exactly -1, alpha is almost exactly - pi. But if you take a look at acos, acos(arg) is a rapidly varying function of its argument when that argument is close to -1 or 1. And at -1 or 1, acosh has infinite slope. So a small change in arg leads to a resulting angle that can easily be off.
Now it's easy to show that
m.(-n) = cos(-pi + alpha)
Let
-pi + alpha = theta
where theta is small. Also so define a new vector q = -n, so q and m are almost identical. Then
m.q = cos(theta) theta = acos(m.q) % angle between m and q
This does not help yet, because here the argument of acos is almost 1, and acos has the same problem as before. But now you have the small quantity
d = m-q
available as the third side of a triangle whose other two sides have length 1 and angle theta between them. So
|d| = 2*sin(theta/2) theta = 2*asin(|d|/2)
asin for small argument is basically linear and has no issues like acos does. For very small |d|,
theta = |d|
0 Comments
See Also
Categories
Find more on Problem-Based Optimization Setup in Help Center and File Exchange
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!